Библиотека Focus-trap предназначена для управления фокусом в веб-приложениях, обеспечивая его ограничение внутри определённого контейнера. Это особенно важно для модальных окон, выпадающих меню, диалогов и других компонентов, где пользовательский фокус не должен покидать активный элемент интерфейса до завершения взаимодействия. Фокусировка гарантирует корректную навигацию с клавиатуры, соответствие стандартам доступности (a11y) и предотвращает случайное взаимодействие с фоновым контентом.
Основная идея заключается в том, чтобы «замкнуть» фокус внутри контейнера, позволяя циклическое перемещение между элементами с помощью клавиши Tab. Любая попытка перемещения за пределы контейнера автоматически возвращается на первый или последний доступный элемент.
Для использования библиотеки необходимо установить пакет через npm или yarn:
npm install focus-trap
Или:
yarn add focus-trap
Создание фокустрапа выполняется следующим образом:
import { createFocusTrap } from 'focus-trap';
const modal = document.getElementById('modal');
const focusTrap = createFocusTrap(modal, {
onActivate: () => modal.classList.add('is-active'),
onDeactivate: () => modal.classList.remove('is-active'),
initialFocus: '#modal input', // элемент, который получит фокус при активации
escapeDeactivates: true, // отключение ловушки по Esc
clickOutsideDeactivates: true // отключение при клике вне контейнера
});
Активация и деактивация фокустрапа:
focusTrap.activate();
focusTrap.deactivate();
1. initialFocus Позволяет указать
элемент, который получит фокус при активации ловушки. Значение может
быть селектором CSS, DOM-элементом или функцией, возвращающей
элемент.
2. fallbackFocus Элемент, на который
фокус будет установлен, если initialFocus недоступен.
Обычно указывается сам контейнер.
3. escapeDeactivates Булево значение,
определяющее, будет ли ловушка деактивирована при нажатии клавиши
Escape.
4. clickOutsideDeactivates Позволяет
закрывать ловушку при клике за пределами контейнера. Принимает булево
значение или функцию, возвращающую булево.
5. allowOutsideClick Разрешает
взаимодействие с элементами вне ловушки, не деактивируя её.
6. returnFocusOnDeactivate
Автоматически возвращает фокус на элемент, который был активен до
активации ловушки.
Focus-trap отслеживает все фокусируемые элементы внутри контейнера. Под фокусируемыми понимаются:
<input>, <select>,
<textarea>, <button><a> с атрибутом hreftabindex="0"Цикличность фокуса обеспечивается логикой, которая при попытке переместиться за последний элемент возвращает фокус на первый, и наоборот.
Пример принудительного перемещения фокуса:
focusTrap.activate();
document.querySelector('#modal input').focus();
Если внутри контейнера добавляются новые элементы после активации
фокустрапа, библиотека корректно учитывает их при навигации с
клавиатуры. Важно убедиться, что новые элементы имеют корректный
tabindex и доступны для фокусировки.
Пример динамического обновления:
const newButton = document.createElement('button');
newButton.textContent = 'Новый элемент';
modal.appendChild(newButton);
После добавления нового элемента фокус автоматически будет включать его в циклическую навигацию.
Для интеграции с React часто используют обертки или хук
useEffect:
import { useEffect, useRef } from 'react';
import { createFocusTrap } from 'focus-trap';
function Modal({ isOpen }) {
const modalRef = useRef(null);
const trapRef = useRef(null);
useEffect(() => {
trapRef.current = createFocusTrap(modalRef.current, { escapeDeactivates: true });
if (isOpen) trapRef.current.activate();
else trapRef.current.deactivate();
}, [isOpen]);
return <div ref={modalRef}>Контент модального окна</div>;
}
В Vue, Angular и других фреймворках используется аналогичный подход с
жизненным циклом компонентов: инициализация в
mounted/ngAfterViewInit, деактивация при
уничтожении компонента.
initialFocus задан неверно, ловушка автоматически
использует fallbackFocus.tabindex="-1" исключены из цикла фокуса.display: none или visibility: hidden
игнорируются.Focus-trap позволяет создавать асинхронные ловушки. Например, при открытии модального окна с задержкой или анимацией:
setTimeout(() => focusTrap.activate(), 300);
Можно комбинировать несколько ловушек с различными условиями
активации, используя allowOutsideClick и кастомные функции
проверки:
clickOutsideDeactivates: (event) => !event.target.closest('.always-active')
Такой подход позволяет гибко управлять доступностью интерфейса без нарушения UX.
Focus-trap является мощным инструментом для контроля фокуса в сложных
интерфейсах. Корректная настройка initialFocus,
fallbackFocus и обработчиков событий обеспечивает полную
доступность и предсказуемое поведение клавиатурной навигации. Правильное
использование предотвращает уход фокуса за пределы активного элемента и
улучшает взаимодействие с приложением.