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;
}
});