power-focusable
v4.1.1
Published
High-precision focus management utility with full composed tree support
Downloads
3,272
Maintainers
Readme
Power Focusable
High-precision focus management utility with full composed tree support. Handles complex focus rules including tabindex ordering, radio groups, inert.
[!NOTE] Supports shadow DOM traversal via the composed tree. Only open shadow roots are included; closed shadow roots are not accessible.
Install
npm i power-focusable// npm
import {
createFocusTrap,
getFocusables,
getNextFocusable,
getPreviousFocusable,
hasFocusable,
inertOutside,
isFocusable,
} from 'power-focusable';
// CDNs
import { ... } 'https://esm.sh/power-focusable'
// or
import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable/+esm';
// or
import { ... } 'https://unpkg.com/power-focusable/dist/index.js';🪄 Options
interface PowerFocusableOptions {
anchor?: Element | null; // default: document.activeElement
composed?: boolean; // default: false
filter?: (element: Element) => boolean; // default: () => true
include?: (element: Element) => boolean; // default: undefined
wrap?: boolean; // default: false
}anchor
Specifies the starting element.
Used by getNextFocusable and getPreviousFocusable.
composed
If true, traverses the composed tree (including shadow DOM; slower)
Used by getFocusables, getNextFocusable, getPreviousFocusable, and hasFocusable.
filter
Custom filter function for excluding elements from focus traversal.
The function should return true to include the element, or false to exclude it.
Used by getFocusables, getNextFocusable, getPreviousFocusable, and hasFocusable.
include
Custom include function for adding elements to focus traversal even if they are not normally focusable.
The function should return true to include the element, or false to ignore it.
Used by getFocusables, getNextFocusable, getPreviousFocusable, and hasFocusable.
wrap
If true, wraps around to the first or last element when reaching the end.
Used by getNextFocusable and getPreviousFocusable.
📦 APIs
createFocusTrap
Creates a keyboard focus trap within the container. Automatically focuses the container itself when possible; otherwise focuses the first available focusable element.
const cleanup = createFocusTrap(container);
// => () => void
//
// container: ElementgetFocusables
Returns all focusable elements within the container.
getFocusables(container);
// => Element[]
//
// container (optional): Element (default: <body>)
// Traverses the composed tree (including shadow DOM; slower)
getFocusables(container, { composed: true });
// Uses custom filter function
getFocusables(container, { filter: (element) => !element.matches('[data-skip-focus]') });getNextFocusable
Returns the next focusable element within the container, starting from document.activeElement.
getNextFocusable(container);
// => Element | null
//
// container (optional): Element (default: <body>)
// Specifies the starting element
getNextFocusable(container, { anchor: document.querySelector('.button') });
// Traverses the composed tree (including shadow DOM; slower)
getNextFocusable(container, { composed: true });
// Uses custom filter function
getNextFocusable(container, { filter: (element) => !element.matches('[data-skip-focus]') });
// Wraps around to the first element when reaching the end
getNextFocusable(container, { wrap: true });getPreviousFocusable
Returns the previous focusable element within the container, starting from document.activeElement.
getPreviousFocusable(container);
// => Element | null
//
// container (optional): Element (default: <body>)
// Specifies the starting element
getPreviousFocusable(container, { anchor: document.querySelector('.button') });
// Traverses the composed tree (including shadow DOM; slower)
getPreviousFocusable(container, { composed: true });
// Uses custom filter function
getPreviousFocusable(container, { filter: (element) => !element.matches('[data-skip-focus]') });
//Wraps around to the last element when reaching the end
getPreviousFocusable(container, { wrap: true });
hasFocusable
Returns whether the container contains at least one focusable element.
hasFocusable(container);
// => boolean
//
// container (optional): Element (default: <body>)
// Traverses the composed tree (including shadow DOM; slower)
hasFocusable(container, { composed: true });
// Uses custom filter function
hasFocusable(container, { filter: (element) => !element.matches('[data-skip-focus]') });inertOutside
Temporarily applies inert to all elements outside the target element. Useful for modals, dialogs, and overlays.
const cleanup = inertOutside(element);
// => () => void
//
// element: ElementisFocusable
Returns whether the given element is focusable.
isFocusable(element);
// => boolean
//
// element: Element
