Библиотека Focus-trap предназначена для управления фокусом внутри заданного DOM-элемента, предотвращая его уход за пределы контейнера. Это особенно важно при работе с модальными окнами, всплывающими панелями и любыми интерактивными компонентами, где требуется удерживать пользователя внутри определенной области интерфейса.
Для создания нового фокус-трапа используется функция
createFocusTrap, которая принимает два аргумента: селектор
или DOM-элемент контейнера и объект опций:
import { createFocusTrap } from 'focus-trap';
const modal = document.querySelector('#modal');
const trap = createFocusTrap(modal, {
escapeDeactivates: true,
clickOutsideDeactivates: true,
initialFocus: '#modal input:first-of-type'
});
Ключевые моменты:
escapeDeactivates — закрывает фокус-трап при нажатии
клавиши Escape.clickOutsideDeactivates — позволяет деактивировать
фокус-трап при клике вне контейнера.initialFocus — задаёт элемент, который получит фокус
при активации трапа.Фокус-трап активируется методом activate() и
деактивируется методом deactivate():
trap.activate();
// Фокус теперь ограничен контейнером #modal
trap.deactivate();
// Фокус возвращается к элементу, который был активен до активации
Если требуется автоматический возврат фокуса к триггеру открытия
модального окна, это реализуется через опцию
returnFocusOnDeactivate:
const trap = createFocusTrap(modal, {
returnFocusOnDeactivate: true
});
Focus-trap автоматически определяет все элементы внутри контейнера,
на которые можно поставить фокус (tabindex ≥ 0,
a[href], button, input,
select, textarea). Дополнительно можно
контролировать поведение через опции:
allowOutsideClick — функция или булево значение,
позволяющее клики вне контейнера, не деактивируя трап.fallbackFocus — элемент для фокусировки, если
initialFocus не найден.const trap = createFocusTrap(modal, {
fallbackFocus: modal,
allowOutsideClick: (event) => event.target.classList.contains('allow-click')
});
В контексте модальных окон и aria-modal правильная организация фокуса критична для доступности. Важно, чтобы:
Пример интеграции с кнопкой открытия:
const openButton = document.querySelector('#openModal');
openButton.addEventListener('click', () => {
trap.activate();
modal.setAttribute('aria-modal', 'true');
modal.style.display = 'block';
});
const closeButton = modal.querySelector('.close');
closeButton.addEventListener('click', () => {
trap.deactivate();
modal.removeAttribute('aria-modal');
modal.style.display = 'none';
});
Если элементы модального окна подгружаются динамически,
initialFocus может быть недоступен на момент активации. Для
этого используется метод trap.activate() с опцией
delay или ручной фокус после рендеринга:
trap.activate();
setTimeout(() => {
const input = modal.querySelector('input:first-of-type');
if(input) input.focus();
}, 0);
Focus-trap поддерживает ряд событий и обратных вызовов:
onActivate — вызывается при активации трапа.onDeactivate — вызывается при деактивации.checkCanFocusTrap — функция, возвращающая Promise,
позволяющая откладывать активацию до готовности элементов.Пример с асинхронной загрузкой:
const trap = createFocusTrap(modal, {
checkCanFocusTrap: () => {
return new Promise(resolve => {
setTimeout(resolve, 100); // дождаться рендера элементов
});
},
onActivate: () => console.log('Focus trap activated'),
onDeactivate: () => console.log('Focus trap deactivated')
});
Для корректного использования с aria-modal важно:
aria-modal="true" к контейнеру при
активации.role="dialog" или
role="alertdialog" для модального окна.Focus-trap не управляет ARIA-атрибутами автоматически, поэтому это нужно делать вручную в обработчиках активации и деактивации.
fallbackFocus, чтобы избежать ошибок
при динамическом контенте.returnFocusOnDeactivate, чтобы не терять
контекст пользователя.Focus-trap позволяет строить полностью доступные модальные интерфейсы, минимизируя риск случайного ухода фокуса и обеспечивая предсказуемое поведение при навигации клавиатурой.