Skip to content

Focus Trap Modal

DodaTech 3 min read

title: "Focus Trap — Keeping Keyboard Focus Inside the Modal" description: "Learn how to implement a focus trap that keeps keyboard focus cycling within the modal dialog, preventing users from tabbing to background content." weight: 5 date: 2026-06-28 lastmod: 2026-06-28 tags: [accessibility, modals]


A focus trap ensures that when a modal is open, pressing Tab cycles through elements inside the modal only, never reaching the background page.

## What You'll Learn

How to implement a focus trap with JavaScript, identify focusable elements, and handle first and last element edge cases.

## Why It Matters

Without a focus trap, keyboard users can Tab out of the modal to background content, losing context and potentially causing unexpected actions.

## Real-World Use

A modal asks "Are you sure you want to delete?" with Cancel and Delete buttons. The user tabs through both buttons. At the Delete button, the next Tab cycles back to Cancel instead of reaching the background page.

## Focus Trap Implementation

```javascript
function trapFocus(element) {
  const focusable = element.querySelectorAll(
    'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])'
  );
  const firstFocusable = focusable[0];
  const lastFocusable = focusable[focusable.length - 1];

  element.addEventListener('keydown', function(e) {
    if (e.key !== 'Tab') return;

    if (e.shiftKey) {
      if (document.activeElement === firstFocusable) {
        e.preventDefault();
        lastFocusable.focus();
      }
    } else {
      if (document.activeElement === lastFocusable) {
        e.preventDefault();
        firstFocusable.focus();
      }
    }
  });

  firstFocusable.focus();
}

Usage

const dialog = document.getElementById('my-modal');
dialog.addEventListener('keydown', focusHandler);

function focusHandler(e) {
  if (e.key !== 'Tab') return;
  const focusable = Array.from(
    dialog.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
  ).filter(el => !el.disabled && el.offsetParent !== null);

  const first = focusable[0];
  const last = focusable[focusable.length - 1];

  if (e.shiftKey && document.activeElement === first) {
    e.preventDefault();
    last.focus();
  } else if (!e.shiftKey && document.activeElement === last) {
    e.preventDefault();
    first.focus();
  }
}

Common Mistakes

1. Focus trap not enabled on open

The focus trap must be attached when the modal opens, not on page load.

2. Focus trap not removed on close

The focus trap event listener must be removed when the modal closes to restore normal tab behavior.

3. Hidden focusable elements in the trap

Screen-reader-only elements or invisible elements should have tabindex="-1" to exclude them from the trap.

4. No initial focus

When the modal opens, focus must move to the first focusable element or the primary action button.

5. Focus trap with dynamic content

If modal content changes dynamically, recalculate the focusable elements.

Practice Questions

1. What keyboard key does a focus trap intercept? The Tab key (and Shift+Tab for reverse navigation).

2. What happens when the user tabs past the last focusable element? Focus wraps back to the first focusable element inside the modal.

3. Why must the focus trap be removed on close? To restore normal page tabbing behavior for elements outside the modal.

Challenge: Implement a focus trap for a modal with 5 focusable elements. Verify wrapping behavior and removal on close.

FAQ

Does the native dialog element need a focus trap?

No. The native dialog element with showModal() has built-in focus trapping.

Should the close button be first in focus order?

Typically, the primary action should receive focus first. The close button should be accessible via Tab.

How do I handle focus trap with dynamically loaded content?

Re-query the focusable elements after content loads. Use MutationObserver to detect DOM changes.

What about hidden elements inside the modal?

Filter elements with offsetParent !== null to exclude hidden elements from the focus trap.

Should I trap focus for non-modal dialogs?

No. Non-modal dialogs should not trap focus. Users should be able to interact with the background.

Mini Project

Build a modal component with a complete focus trap. Include input fields, buttons, and links inside the modal. Verify Tab wrapping works correctly.

What's Next

Learn where to set Initial Focus when a modal opens to provide the best user experience for keyboard and screen reader users.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro