Trap focus within a DOM node for accessible modals and interactive UI components.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Core functionality for creating and managing focus traps on DOM elements. The createFocusTrap function is the primary entry point for establishing focus boundaries within your application.
Creates a new focus trap on specified element(s) that will contain and cycle focus within their boundaries.
/**
* Creates a new focus trap on specified element(s)
* @param elements - Container element(s) for the focus trap
* @param userOptions - Configuration options for the trap
* @returns FocusTrap instance with control methods
*/
function createFocusTrap(
elements: HTMLElement | SVGElement | string | Array<HTMLElement | SVGElement | string>,
userOptions?: Options
): FocusTrap;Usage Examples:
import { createFocusTrap } from 'focus-trap';
// Single element trap
const trap1 = createFocusTrap('#modal');
// Multiple container trap
const trap2 = createFocusTrap(['#sidebar', '#main-content']);
// Direct DOM element reference
const element = document.getElementById('dialog');
const trap3 = createFocusTrap(element);
// With configuration options
const trap4 = createFocusTrap('#popup', {
initialFocus: '#first-input',
escapeDeactivates: false,
clickOutsideDeactivates: true
});Focus traps require at least one container with at least one tabbable/focusable element.
Valid Container Types:
document.querySelector()Container Requirements:
fallbackFocus option// Valid: Element with focusable content
const trapWithInputs = createFocusTrap('#form');
// Valid: Using fallback focus for empty container
const trapWithFallback = createFocusTrap('#empty-container', {
fallbackFocus: '#empty-container', // Make container itself focusable with tabindex="-1"
});
// Invalid: No focusable elements and no fallback
// This will throw an error when activated
const invalidTrap = createFocusTrap('#empty-div');The returned focus trap instance provides control methods and status properties.
interface FocusTrap {
/** Whether the trap is currently active and managing focus */
readonly active: boolean;
/** Whether the trap is currently paused (active but not managing focus) */
readonly paused: boolean;
/** Activate the focus trap */
activate(activateOptions?: ActivateOptions): FocusTrap;
/** Deactivate the focus trap */
deactivate(deactivateOptions?: DeactivateOptions): FocusTrap;
/** Pause the trap without fully deactivating */
pause(pauseOptions?: PauseOptions): FocusTrap;
/** Resume a paused trap */
unpause(unpauseOptions?: UnpauseOptions): FocusTrap;
/** Update the container elements for this trap */
updateContainerElements(
containerElements: HTMLElement | SVGElement | string | Array<HTMLElement | SVGElement | string>
): FocusTrap;
}When using multiple containers, focus cycles through them in the provided order.
// Focus cycles: button1 -> input1 -> button2 -> input2 -> button1...
const multiTrap = createFocusTrap([
'#container1', // contains: button1, input1
'#container2' // contains: button2, input2
]);
// Update containers dynamically
multiTrap.updateContainerElements(['#new-container1', '#new-container2']);Focus trap creation and activation can throw errors in certain scenarios:
fallbackFocus is providedtry {
const trap = createFocusTrap('#my-container');
trap.activate();
} catch (error) {
if (error.message.includes('at least one tabbable element')) {
// Handle missing focusable elements
console.log('Container needs focusable elements or fallbackFocus option');
} else if (error.message.includes('positive tabindex')) {
// Handle positive tabindex in multi-container scenario
console.log('Multi-container traps cannot use positive tabindex values');
} else if (error.message.includes('Could not find')) {
// Handle invalid selector
console.log('CSS selector did not match any elements');
}
}Focus-trap uses mutation observers to track DOM changes within containers. For optimal performance:
// For large containers, disable display checking
const largeTrap = createFocusTrap('#large-data-table', {
tabbableOptions: {
displayCheck: 'none' // Skip expensive visibility checks
}
});
// For containers with many focusable elements
const complexTrap = createFocusTrap('#complex-form', {
tabbableOptions: {
displayCheck: 'non-zero-area' // Faster than 'full' but still accurate
}
});Focus-trap supports all modern browsers. For older browser support:
// Polyfill for older browsers if needed
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector;
}
// IE11 compatibility - avoid arrow functions in options
const trap = createFocusTrap('#modal', {
isKeyForward: function(event) {
return event.key === 'Tab' && !event.shiftKey;
}
});