or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdindex.mdtrap-control.mdtrap-creation.md
tile.json

trap-creation.mddocs/

Trap Creation and Management

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.

Capabilities

createFocusTrap Function

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

Container Element Requirements

Focus traps require at least one container with at least one tabbable/focusable element.

Valid Container Types:

  • DOM Node: Direct HTMLElement or SVGElement reference
  • Selector String: CSS selector passed to document.querySelector()
  • Array: Multiple containers where Tab cycles through all in order

Container Requirements:

  • Must contain at least one tabbable element OR use fallbackFocus option
  • Containers are processed in array order for multi-container traps
  • Each container can have any number of focusable/tabbable elements
// 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');

FocusTrap Interface

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

Multi-Container Traps

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']);

Error Conditions

Focus trap creation and activation can throw errors in certain scenarios:

  • No Tabbable Elements: If no tabbable elements exist and no fallbackFocus is provided
  • Invalid Selectors: If a selector string doesn't match any elements
  • Positive Tabindex with Multiple Containers: Multi-container traps don't support positive tabindex values
try {
  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');
  }
}

Performance Considerations

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

Browser Compatibility

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