93Implement an accessible focus trap for a dialog
Problem
When a modal dialog is open, keyboard focus must stay inside it. Implement createFocusTrap(container, { initialFocus, returnFocus = true, onEscape } = {}) returning { activate(), deactivate() }:
activate()remembers the element that had focus, then focusesinitialFocus(an element), or the first tabbable element in the container, or the container itself (give ittabIndex = -1if needed).- While active,
Tabon the last tabbable element moves focus to the first, andShift+Tabon the first moves to the last. - If focus somehow lands outside the container (a click, a script), move it back inside.
EscapecallsonEscape.deactivate()removes every listener and, ifreturnFocus, focuses the element that had focus beforeactivate().
Tabbable means: a[href], button, input, select, textarea, [tabindex] and [contenteditable] elements that are not disabled, not tabindex="-1", and not hidden (inside [hidden] or with display: none).
Try it yourself
Write your solution, run it, then submit to check it against the tests.
Step-by-step
Query the candidates with one selector, then filter out disabled ones, tabindex="-1", and hidden ones (el.closest("[hidden]") or getComputedStyle(el).display === "none", and elements with no client rects). Compute the list at the moment of each Tab press, because the dialog content can change while it is open.
Unlock the full solution
What's inside
- Step-by-step
- Approach
- Solution
- Complexity
- Edge cases
- Follow-up questions
- Tests
Includes 2 code listings, 5 edge cases, 4 follow-up questions.
One-time payment by UPI, card or net banking. Launch price.