Библиотека Focus-trap предназначена для управления фокусом внутри модальных окон и других интерактивных контейнеров. Основная задача — предотвратить уход фокуса за пределы активного интерфейса, особенно при использовании клавиатуры. Это критично для доступности (accessibility), поскольку пользователи с клавиатурой или экранными читалками должны оставаться внутри контекста активного элемента.
Focus-trap работает через перехват событий Tab и
Shift+Tab, циклически перемещая фокус между
фокусируемыми элементами внутри контейнера. Поддерживаются кнопки,
ссылки, поля ввода, textarea и любые элементы с атрибутом
tabindex="0" или большим положительным значением.
Для создания фокустрапа требуется указать контейнер и, при необходимости, конфигурацию. Пример:
import { createFocusTrap } from 'focus-trap';
const modal = document.getElementById('modal');
const focusTrap = createFocusTrap(modal, {
escapeDeactivates: true,
clickOutsideDeactivates: true,
allowOutsideClick: true,
});
focusTrap.activate();
Ключевые параметры:
escapeDeactivates — при нажатии клавиши
Esc фокус-трап деактивируется.clickOutsideDeactivates — клик вне контейнера снимает
фокус-трап.allowOutsideClick — позволяет обработчикам внешнего
клика выполняться без нарушения фокус-трапа.Методы:
activate() — включает фокус-трап.deactivate() — выключает фокус-трап.pause() / unpause() — временно
приостанавливают управление фокусом, не снимая его полностью.Сложность возникает при работе с несколькими модальными окнами, открывающимися одно поверх другого. Простая активация нового фокус-трапа поверх старого может нарушить логику циклического фокуса, если старый трап продолжает слушать события.
unpause() или повторную
активацию.Пример с двумя модальными окнами:
const outerModal = document.getElementById('outerModal');
const innerModal = document.getElementById('innerModal');
const outerTrap = createFocusTrap(outerModal, { escapeDeactivates: false });
const innerTrap = createFocusTrap(innerModal, { escapeDeactivates: true });
// Открытие внешнего модального окна
outerTrap.activate();
// Открытие внутреннего модального окна
document.getElementById('openInner').addEventListener('click', () => {
outerTrap.pause(); // временно приостанавливаем старый трап
innerTrap.activate(); // активируем вложенный трап
});
// Закрытие внутреннего модального окна
document.getElementById('closeInner').addEventListener('click', () => {
innerTrap.deactivate();
outerTrap.unpause(); // возвращаем управление внешнему трапу
});
Для вложенных модальных окон важно, чтобы нажатие Esc закрывало только верхнее окно, не прерывая фокус-трап нижнего уровня:
const innerTrap = createFocusTrap(innerModal, {
escapeDeactivates: true,
onDeactivate: () => {
outerTrap.unpause();
}
});
Событие onDeactivate позволяет автоматически возвращать
фокус к предыдущему контейнеру, сохраняя корректный цикл.
Если модальное окно создаётся динамически, фокус-трап необходимо создавать после вставки элемента в DOM:
const dynamicModal = document.createElement('div');
dynamicModal.id = 'dynamicModal';
document.body.appendChild(dynamicModal);
const dynamicTrap = createFocusTrap(dynamicModal);
dynamicTrap.activate();
Для вложенных динамических модалей следует использовать аналогичную стратегию паузы и активации.
Focus-trap по умолчанию учитывает все стандартные фокусируемые элементы. Можно вручную задать элементы, через которые будет циклировать фокус:
const customTrap = createFocusTrap(modal, {
tabbableOptions: {
displayCheck: 'full', // проверка видимости элементов
includeContainer: true // включить сам контейнер в цикл
}
});
Опции tabbableOptions позволяют фильтровать элементы по
критериям видимости и включать кастомные селекторы для управления
фокусом.
Если модальное окно закрывается с анимацией, деактивацию фокус-трапа лучше выполнять после завершения анимации. Это предотвращает мгновенное снятие фокуса до завершения визуальных переходов.
modal.addEventListener('transitionend', () => {
trap.deactivate();
});
onDeactivate для автоматического возврата
управления.tabbableOptions для исключения невидимых
или неактивных элементов из цикла фокуса.Эти подходы позволяют создавать сложные интерфейсы с несколькими уровнями модальных окон, сохраняя корректное управление клавиатурным фокусом и соответствие стандартам доступности.