or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

tessl/npm-focus-trap

Trap focus within a DOM node for accessible modals and interactive UI components.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/focus-trap@7.6.x

To install, run

npx @tessl/cli install tessl/npm-focus-trap@7.6.0

index.mddocs/

Focus Trap

Focus Trap is a vanilla JavaScript library that traps focus within a DOM node, essential for building accessible modals, menus, and interactive UI components. It handles Tab and Shift+Tab navigation cycles within designated containers, blocks clicks outside the trap, supports Escape key deactivation, and automatically restores focus to the previously focused element when deactivated.

Package Information

  • Package Name: focus-trap
  • Package Type: npm
  • Language: JavaScript/TypeScript
  • Installation: npm install focus-trap

Core Imports

import { createFocusTrap } from 'focus-trap';

For CommonJS:

const { createFocusTrap } = require('focus-trap');

For UMD (browser):

<script src="https://unpkg.com/tabbable/dist/index.umd.js"></script>
<script src="https://unpkg.com/focus-trap/dist/focus-trap.umd.js"></script>
<script>
  // Available as global: focusTrap.createFocusTrap
</script>

Basic Usage

import { createFocusTrap } from 'focus-trap';

// Create a focus trap on a modal element
const container = document.getElementById('modal');
const focusTrap = createFocusTrap('#modal', {
  onActivate: () => container.classList.add('is-active'),
  onDeactivate: () => container.classList.remove('is-active'),
});

// Activate the trap when modal opens
document.getElementById('open-modal').addEventListener('click', () => {
  focusTrap.activate();
});

// Deactivate when modal closes
document.getElementById('close-modal').addEventListener('click', () => {
  focusTrap.deactivate();
});

Architecture

Focus Trap is built around several key components:

  • Trap Creation: createFocusTrap() function creates trap instances for specific containers
  • Focus Management: Automatic focus cycling within trap boundaries using Tab/Shift+Tab keys
  • Event Handling: Mouse, keyboard, and focus event interception to maintain trap boundaries
  • Stack Management: Coordination between multiple active traps with automatic pausing/unpausing
  • Tabbable Integration: Uses the 'tabbable' library to determine focusable and tabbable elements
  • Shadow DOM Support: Compatible with Shadow DOM elements and custom focus behavior

Capabilities

Trap Creation and Management

Core functionality for creating and managing focus traps on DOM elements.

function createFocusTrap(
  elements: HTMLElement | SVGElement | string | Array<HTMLElement | SVGElement | string>,
  userOptions?: Options
): FocusTrap;

Trap Creation

Trap Control Methods

Methods for activating, deactivating, pausing, and updating focus traps.

interface FocusTrap {
  readonly active: boolean;
  readonly paused: boolean;
  activate(activateOptions?: ActivateOptions): FocusTrap;
  deactivate(deactivateOptions?: DeactivateOptions): FocusTrap;
  pause(pauseOptions?: PauseOptions): FocusTrap;
  unpause(unpauseOptions?: UnpauseOptions): FocusTrap;
  updateContainerElements(
    containerElements: HTMLElement | SVGElement | string | Array<HTMLElement | SVGElement | string>
  ): FocusTrap;
}

Trap Control

Configuration Options

Extensive configuration system for customizing trap behavior, focus targets, and event handling.

interface Options {
  // Lifecycle callbacks
  onActivate?: () => void;
  onPostActivate?: () => void;
  onDeactivate?: () => void;
  onPostDeactivate?: () => void;
  
  // Focus management
  initialFocus?: FocusTargetOrFalse | undefined | (() => void);
  fallbackFocus?: FocusTarget;
  returnFocusOnDeactivate?: boolean;
  setReturnFocus?: FocusTargetValueOrFalse | ((previousActiveElement: HTMLElement | SVGElement) => FocusTargetValueOrFalse);
  
  // User interaction
  escapeDeactivates?: boolean | ((event: KeyboardEvent) => boolean);
  clickOutsideDeactivates?: boolean | ((event: MouseEvent | TouchEvent) => boolean);
  allowOutsideClick?: boolean | ((event: MouseEvent | TouchEvent) => boolean);
  
  // Other options
  preventScroll?: boolean;
  delayInitialFocus?: boolean;
  document?: Document;
  tabbableOptions?: FocusTrapTabbableOptions;
  trapStack?: Array<FocusTrap>;
  isKeyForward?: (event: KeyboardEvent) => boolean;
  isKeyBackward?: (event: KeyboardEvent) => boolean;
}

Configuration

Tabbable Integration

Focus-trap relies on the tabbable library to identify focusable and tabbable elements within containers.

// Functions from tabbable used internally by focus-trap
import {
  tabbable,     // Gets tabbable elements in tab order
  focusable,    // Gets all focusable elements
  isFocusable,  // Checks if single element is focusable
  isTabbable,   // Checks if single element is tabbable
  getTabIndex   // Gets computed tab index value
} from 'tabbable';

The tabbableOptions configuration passes through to these tabbable functions to control:

  • Display checking: How strictly to verify element visibility
  • Shadow DOM: How to traverse shadow DOM boundaries
  • Container inclusion: Whether container elements can receive focus

Error Handling

Focus-trap throws errors in specific scenarios that should be handled:

// Common error conditions
type FocusTrapError = 
  | 'NO_TABBABLE_ELEMENTS'     // No focusable elements found and no fallbackFocus
  | 'INVALID_SELECTOR'         // CSS selector doesn't match any elements
  | 'POSITIVE_TABINDEX'        // Positive tabindex in multi-container setup
  | 'CONTAINER_NOT_FOUND';     // Container element no longer in DOM

// Error handling pattern
try {
  const trap = createFocusTrap('#container');
  trap.activate();
} catch (error) {
  if (error.message.includes('at least one tabbable element')) {
    // Handle missing focusable elements
    console.log('Add focusable elements or use fallbackFocus option');
  }
}

Core Types

type FocusTargetValue = HTMLElement | SVGElement | string;
type FocusTargetValueOrFalse = FocusTargetValue | false;
type FocusTarget = FocusTargetValue | (() => FocusTargetValue);
type FocusTargetOrFalse = FocusTargetValueOrFalse | (() => FocusTargetValueOrFalse);

interface FocusTrapTabbableOptions {
  /** How to check if an element is displayed (affects performance) */
  displayCheck?: 'full' | 'legacy-full' | 'non-zero-area' | 'none';
  /** Function to get shadow root for shadow DOM elements */
  getShadowRoot?: (node: Element) => ShadowRoot | boolean;
  /** Whether to include the container element itself as tabbable */
  includeContainer?: boolean;
}

type MouseEventToBoolean = (event: MouseEvent | TouchEvent) => boolean;
type KeyboardEventToBoolean = (event: KeyboardEvent) => boolean;

// Activation/Deactivation/Pause option types
interface ActivateOptions {
  onActivate?: () => void;
  onPostActivate?: () => void;
  checkCanFocusTrap?: (containers: Array<HTMLElement | SVGElement>) => Promise<void>;
}

interface DeactivateOptions {
  returnFocus?: boolean;
  onDeactivate?: () => void;
  onPostDeactivate?: () => void;
  checkCanReturnFocus?: (trigger: HTMLElement | SVGElement) => Promise<void>;
}

interface PauseOptions {
  onPause?: () => void;
  onPostPause?: () => void;
}

interface UnpauseOptions {
  onUnpause?: () => void;
  onPostUnpause?: () => void;
}