Библиотека Focus-trap предназначена для управления фокусом в интерактивных элементах веб-страницы, таких как модальные окна, выпадающие меню и кастомные диалоги. Главная цель — ограничить навигацию с клавиатуры (Tab, Shift+Tab) выбранной областью, предотвращая случайное перемещение фокуса за пределы активного компонента.
Ключевые моменты:
aria-атрибутами, улучшая доступность.Создание focus-trap осуществляется с помощью функции
createFocusTrap. Простейший пример:
import { createFocusTrap } from 'focus-trap';
const modal = document.getElementById('modal');
const trap = createFocusTrap(modal, {
onActivate: () => modal.classList.add('is-active'),
onDeactivate: () => modal.classList.remove('is-active'),
clickOutsideDeactivates: true
});
// Активация trap
trap.activate();
// Деактивация trap
trap.deactivate();
Пояснения параметров:
onActivate и onDeactivate — функции
обратного вызова, вызываемые при активации и деактивации.clickOutsideDeactivates — разрешает закрытие trap при
клике вне контейнера.returnFocusOnDeactivate (по умолчанию
true) — возвращает фокус к элементу, который его имел до
активации.Когда активен focus-trap, важно корректно управлять прокруткой страницы, чтобы пользователь не мог прокрутить фон, оставаясь внутри модального окна. Есть несколько подходов:
body.modal-open {
overflow: hidden;
}
При активации trap добавляется класс modal-open к
<body>, при деактивации — удаляется. Этот метод
предотвращает скролл всего документа, оставляя прокрутку возможной
только внутри модального окна.
overflow-y: auto. Focus-trap не управляет скроллом
контейнера автоматически, но корректно перемещает фокус по его
элементам.#modal {
max-height: 80vh;
overflow-y: auto;
}
initialFocus и
fallbackFocus для управления тем, какой элемент получает
фокус при активации. Это важно, если первый интерактивный элемент
находится за пределами видимой области контейнера. Например:const trap = createFocusTrap(modal, {
initialFocus: '#modal input:first-of-type',
fallbackFocus: '#modal'
});
При активации trap браузер автоматически скроллит к элементу с фокусом.
Focus-trap поддерживает динамическое управление состоянием, что особенно важно для сложных интерфейсов с несколькими модальными окнами или всплывающими меню.
trap.activate()trap.deactivate()const trap = createFocusTrap(modal, {
escapeDeactivates: true
});
clickOutsideDeactivates: trueFocus-trap предоставляет гибкие опции для управления поведением:
tabindex="-1" на элементы вне trap, чтобы исключить их из
цикла табуляции.const trap = createFocusTrap(modal, {
setReturnFocus: document.querySelector('#openModalButton')
});
Если содержимое контейнера меняется после активации trap (например,
добавляются новые поля формы), рекомендуется вызвать
trap.updateTabbableNodes(). Это обновит список фокусируемых
элементов и сохранит корректную навигацию.
trap.updateTabbableNodes();
Эта операция обязательна при динамическом рендеринге, иначе новые элементы будут игнорироваться в цикле табуляции.
aria-hidden="true" на фоновых
элементах при активном trap, чтобы скринридеры не перемещались за
пределы активного контейнера.role="dialog" или role="menu" для
контейнера, чтобы обозначить его семантически.const modal = document.getElementById('modal');
const openBtn = document.getElementById('openModal');
const closeBtn = modal.querySelector('.close');
const trap = createFocusTrap(modal, {
escapeDeactivates: true,
clickOutsideDeactivates: true,
initialFocus: modal.querySelector('input'),
onActivate: () => document.body.classList.add('modal-open'),
onDeactivate: () => document.body.classList.remove('modal-open'),
setReturnFocus: openBtn
});
openBtn.addEventListener('click', () => trap.activate());
closeBtn.addEventListener('click', () => trap.deactivate());
Этот подход обеспечивает:
aria.Фокус в таком случае корректно удерживается внутри окна, а взаимодействие с остальной страницей становится невозможным до деактивации trap.