Библиотека Focus-trap предназначена для управления фокусом внутри ограниченной области DOM, обычно используемой для модальных окон, диалогов, всплывающих меню и других интерактивных компонентов. Основная задача — предотвратить уход фокуса за пределы контейнера, сохраняя корректную навигацию с клавиатуры и улучшая доступность интерфейса.
Ключевой принцип работы — установка ловушки фокуса
(focus trap) на определённый элемент и перенаправление
событий Tab и Shift+Tab для циклической
навигации внутри него.
Для создания ловушки фокуса используется функция
createFocusTrap, принимающая два параметра:
import { createFocusTrap } from 'focus-trap';
const modal = document.getElementById('modal');
const trap = createFocusTrap(modal, {
escapeDeactivates: true,
clickOutsideDeactivates: true,
initialFocus: '#inputName',
fallbackFocus: modal,
onActivate: () => console.log('Trap activated'),
onDeactivate: () => console.log('Trap deactivated')
});
Ключевые опции:
escapeDeactivates — отключает ловушку при нажатии
клавиши Escape.clickOutsideDeactivates — позволяет деактивировать
ловушку при клике вне контейнера.initialFocus — элемент, который получает фокус при
активации.fallbackFocus — элемент для фокуса, если указанный
initialFocus не найден.onActivate и onDeactivate — колбэки,
вызываемые при включении и выключении ловушки.Focus-trap предоставляет несколько хуков для интеграции кастомной логики при изменении состояния фокуса.
Эти колбэки используются для запуска дополнительных действий при активации и деактивации ловушки:
const trap = createFocusTrap(modal, {
onActivate: () => {
modal.classList.add('active');
console.log('Фокус заблокирован внутри модального окна');
},
onDeactivate: () => {
modal.classList.remove('active');
console.log('Фокус возвращён пользователю');
}
});
Часто их используют для управления стилями, анимациями и состояниями интерфейса.
Эти события вызываются после того, как фокус фактически был перемещён или возвращён:
const trap = createFocusTrap(modal, {
onPostActivate: () => {
console.log('Фокус установлен на первый интерактивный элемент');
},
onPostDeactivate: () => {
console.log('Фокус вернулся на исходный элемент');
}
});
Используются для синхронизации с внешними библиотеками или дополнительной логики после завершения основных действий.
Позволяет реагировать на попытку выхода фокуса за пределы контейнера, даже если ловушка активна:
const trap = createFocusTrap(modal, {
onFocusOutside: (event) => {
console.log('Попытка ухода фокуса:', event.target);
event.preventDefault(); // блокирует уход фокуса
}
});
Эта опция полезна для логики валидации или ограничения взаимодействия с интерфейсом.
Можно интегрировать асинхронные действия при активации или деактивации ловушки:
const trap = createFocusTrap(modal, {
onActivate: async () => {
await fetch('/api/log-activation');
console.log('Ловушка активирована и лог отправлен на сервер');
},
onDeactivate: async () => {
await fetch('/api/log-deactivation');
console.log('Ловушка деактивирована и лог отправлен на сервер');
}
});
Асинхронные колбэки позволяют реализовать динамическое управление интерфейсом, например, загрузку данных перед блокировкой фокуса.
Focus-trap автоматически перехватывает клавиши Tab и
Shift+Tab для циклической навигации. Для расширенной
кастомизации можно использовать onKeyDown:
const trap = createFocusTrap(modal, {
onKeyDown: (event) => {
if (event.key === 'Enter') {
console.log('Нажата клавиша Enter внутри ловушки');
}
}
});
Это позволяет обрабатывать любые клавиатурные действия внутри контейнера без выхода за его пределы.
Если содержимое контейнера меняется динамически, ловушка корректно отслеживает новые интерактивные элементы при каждом цикле:
const trap = createFocusTrap(modal, {
allowOutsideClick: true,
tabbableOptions: { displayCheck: 'none' } // игнорирует скрытые элементы
});
Опция tabbableOptions позволяет задавать фильтры для
интерактивных элементов, например игнорировать скрытые или неактивные
кнопки.
Для сложных интерфейсов с несколькими модальными окнами можно создавать несколько ловушек и активировать их по очереди:
const trap1 = createFocusTrap(modal1);
const trap2 = createFocusTrap(modal2);
trap1.activate(); // первый модальный
trap2.activate(); // второй модальный, автоматически блокирует первый
Focus-trap гарантирует корректное переключение фокуса и предотвращает конфликт активных ловушек.
Использование кастомных обработчиков в Focus-trap
позволяет не только удерживать фокус внутри элемента, но и интегрировать
дополнительные бизнес-логики, синхронизировать визуальные состояния и
обрабатывать события клавиатуры. Гибкая система колбэков
(onActivate, onDeactivate,
onPostActivate, onFocusOutside) делает
библиотеку мощным инструментом для создания доступных и управляемых
интерфейсов.