High-precision focus management utility with shadow DOM support. Handles complex focus rules including tabindex ordering, radio groups, etc.
Note
Supports shadow DOM traversal via the composed tree. Only open shadow roots are included; closed shadow roots are not accessible.
npm i power-focusable// npm
import {
getFocusables,
getNextFocusable,
getPreviousFocusable,
hasFocusable,
isFocusable,
} from 'power-focusable';
// CDNs
import { ... } 'https://esm.sh/power-focusable'
// or
import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable/dist/index.js';
// or
import { ... } 'https://unpkg.com/power-focusable/dist/index.js';interface PowerFocusableOptions {
active?: HTMLElement | null; // default: document.activeElement
composed?: boolean; // default: false
wrap?: boolean; // default: false
}Specifies the starting element.
Used by getNextFocusable and getPreviousFocusable.
If true, traverses the composed tree (including shadow DOM; slower)
Used by getFocusables, getNextFocusable, getPreviousFocusable, and hasFocusable.
If true, wraps around to the first or last element when reaching the end.
Used by getNextFocusable and getPreviousFocusable.
Returns all focusable elements within the container.
getFocusables(container);
// => HTMLElement[]
//
// container (optional): HTMLElement (default: <body>)
// Traverses the composed tree (including shadow DOM; slower)
getFocusables(container, { composed: true });Returns the next focusable element within the container, starting from document.activeElement.
getNextFocusable(container);
// => HTMLElement | null
//
// container (optional): HTMLElement (default: <body>)
// Specifies the starting element
getNextFocusable(container, { active: document.querySelector('.button') });
// Traverses the composed tree (including shadow DOM; slower)
getNextFocusable(container, { composed: true });
// Wraps around to the first element when reaching the end
getNextFocusable(container, { wrap: true });Returns the previous focusable element within the container, starting from document.activeElement.
getPreviousFocusable(container);
// => HTMLElement | null
//
// container (optional): HTMLElement (default: <body>)
// Specifies the starting element
getPreviousFocusable(container, { active: document.querySelector('.button') });
// Traverses the composed tree (including shadow DOM; slower)
getPreviousFocusable(container, { composed: true });
//Wraps around to the last element when reaching the end
getPreviousFocusable(container, { wrap: true });Returns whether the container contains at least one focusable element.
hasFocusable(container);
// => boolean
//
// container (optional): HTMLElement (default: <body>)
// Traverses the composed tree (including shadow DOM; slower)
hasFocusable(container, { composed: true });Returns whether the given element is focusable.
isFocusable(element);
// => boolean
//
// element: HTMLElement