Метод activate() в библиотеке
Focus-trap запускает основную функциональность ловушки
фокуса. После его вызова область интерфейса, переданная при создании
ловушки, становится единственным пространством, в пределах которого
разрешено перемещение фокуса клавиатуры. Любые попытки переместить фокус
за пределы этой области автоматически перехватываются и возвращаются
обратно внутрь контейнера.
Механизм используется для реализации модальных окон, диалогов подтверждения, всплывающих панелей, боковых меню и других элементов интерфейса, где требуется временно ограничить навигацию пользователя только частью страницы.
activate() переводит объект ловушки в активное состояние
и инициирует все связанные с этим процессы:
Метод является центральной точкой управления жизненным циклом ловушки фокуса.
trap.activate(options?)
Метод может вызываться без параметров или с объектом конфигурации, позволяющим временно изменить настройки активации.
Возвращаемое значение отсутствует.
Внутренний процесс работы activate() состоит из
нескольких этапов.
1. Проверка состояния ловушки
Если ловушка уже активна, повторная активация не выполняется. Это предотвращает дублирование обработчиков событий и конфликт логики.
2. Сохранение текущего фокуса
Перед активацией библиотека запоминает элемент, который был сфокусирован ранее. Это необходимо для корректного восстановления фокуса при деактивации ловушки.
3. Определение фокусируемых элементов
Focus-trap использует вспомогательную библиотеку
tabbable, которая анализирует DOM-структуру контейнера и
определяет список элементов, доступных для навигации через клавишу
Tab.
В список входят:
<button><a> с атрибутом href<input>, <select>,
<textarea>tabindexЭлементы со свойствами disabled, hidden или
tabindex="-1" исключаются из списка.
4. Назначение обработчиков
После активации устанавливаются слушатели событий:
keydownfocusinmousedown / touchstart (в зависимости от
конфигурации)Эти обработчики обеспечивают контроль перемещения фокуса.
5. Перенос фокуса
Фокус перемещается на элемент, определённый настройками:
initialFocusfallbackFocusЕсли ни один элемент не найден, используется резервный элемент.
import { createFocusTrap } from 'focus-trap';
const modal = document.querySelector('#modal');
const trap = createFocusTrap(modal);
trap.activate();
После вызова activate() клавиша Tab
начинает перемещать фокус только между элементами внутри
#modal. При попытке выйти за границы контейнера фокус
возвращается обратно.
Ключевая функция ловушки — циклическая навигация по фокусируемым элементам.
Предположим, контейнер содержит три элемента:
[Input] → [Button] → [Link]
При нажатии Tab на последнем элементе:
[Link] → Tab → [Input]
При использовании Shift + Tab на первом элементе:
[Input] → Shift+Tab → [Link]
Такой цикл создаёт замкнутую область навигации.
Событие focusin используется для контроля ситуаций,
когда фокус может оказаться вне ловушки.
Это происходит, например:
element.focus()Алгоритм проверки:
onActivateМетод activate() может вызывать пользовательскую функцию
при активации ловушки.
const trap = createFocusTrap(modal, {
onActivate() {
console.log('Trap activated');
}
});
Функция выполняется сразу после перехода ловушки в активное состояние.
onPostActivateИногда требуется выполнить действия после установки
фокуса. Для этого используется onPostActivate.
const trap = createFocusTrap(modal, {
onPostActivate() {
console.log('Focus already set');
}
});
Разница между событиями:
| Событие | Момент вызова |
|---|---|
onActivate |
сразу после активации |
onPostActivate |
после установки фокуса |
Метод activate() автоматически выбирает элемент для
начального фокуса.
Приоритет выбора следующий:
initialFocusfallbackFocusconst trap = createFocusTrap(modal, {
initialFocus: '#first-input'
});
После активации фокус сразу перемещается на элемент с идентификатором
first-input.
fallbackFocusИногда контейнер может не содержать фокусируемых элементов. В этом случае требуется резервный элемент.
const trap = createFocusTrap(modal, {
fallbackFocus: '#modal'
});
Если список tabbable-элементов пуст, фокус будет установлен на контейнер.
Для этого контейнер должен иметь:
<div id="modal" tabindex="-1">
Метод activate() может принимать собственные параметры,
которые переопределяют конфигурацию ловушки только для текущего
запуска.
trap.activate({
onActivate() {
console.log('Activated with custom options');
}
});
Такие параметры не изменяют исходную конфигурацию объекта.
Если ловушка уже активна, вызов activate() не выполняет
повторную инициализацию.
Это предотвращает:
Проверка активности выполняется внутри внутреннего состояния ловушки.
Focus-trap поддерживает стек активных ловушек. При активации новой ловушки предыдущая временно приостанавливается.
Пример:
trap1.activate();
trap2.activate();
В этом случае:
trap1 приостанавливаетсяtrap2 становится активнойПосле деактивации trap2 первая ловушка автоматически
восстанавливается.
Такой механизм особенно полезен при вложенных модальных окнах.
Во время активации обрабатываются клики за пределами контейнера.
Поведение определяется параметром
clickOutsideDeactivates.
const trap = createFocusTrap(modal, {
clickOutsideDeactivates: true
});
При клике вне контейнера ловушка автоматически деактивируется.
escapeDeactivatesЧасто требуется закрытие модального окна клавишей
Escape.
const trap = createFocusTrap(modal, {
escapeDeactivates: true
});
При нажатии Escape:
Focus-trap запоминает элемент, который был активен до вызова
activate().
После деактивации выполняется восстановление фокуса:
Element A → activate() → Trap → deactivate() → Element A
Поведение можно изменить с помощью параметра
returnFocusOnDeactivate.
В некоторых интерфейсах фокусируемые элементы появляются позже, например после загрузки данных.
В этом случае используется опция delayInitialFocus.
const trap = createFocusTrap(modal, {
delayInitialFocus: true
});
Это позволяет дождаться обновления DOM перед установкой фокуса.
import { createFocusTrap } from 'focus-trap';
const modal = document.querySelector('#modal');
const openButton = document.querySelector('#open');
const trap = createFocusTrap(modal, {
initialFocus: '#name',
escapeDeactivates: true,
clickOutsideDeactivates: true
});
openButton.addEventListener('click', () => {
modal.classList.add('active');
trap.activate();
});
HTML-структура:
<div id="modal" class="modal">
<input id="name">
<button>Submit</button>
<button>Cancel</button>
</div>
После открытия модального окна:
Tab перемещает фокус только внутри окнаEscape закрывает ловушкуFocus-trap корректно работает с элементами внутри Shadow DOM, если они доступны для табуляции. Библиотека анализирует распределённые узлы и поддерживает вложенные структуры компонентов.
Однако при сложных веб-компонентах может потребоваться явная
настройка fallbackFocus.
Активация ловушки требует:
Для больших контейнеров это может быть заметной операцией. Поэтому ловушки обычно создаются только для элементов, которые действительно требуют ограничения фокуса.
1. Отсутствие фокусируемых элементов
Без fallbackFocus активация может привести к ошибке.
2. Невозможность фокусировки контейнера
Контейнер должен иметь tabindex="-1".
3. Конфликт с другими скриптами
Некоторые UI-библиотеки могут программно менять фокус.
4. Активация до появления DOM
Если контейнер ещё не добавлен в документ, ловушка не сможет определить фокусируемые элементы.
activate() в жизненном цикле Focus-trapЖизненный цикл ловушки состоит из трёх основных этапов:
createFocusTrap)activate)deactivate)Метод activate() запускает весь механизм ограничения
фокуса и переводит интерфейс в режим контролируемой навигации. Именно на
этом этапе происходит подключение всех обработчиков и формирование
замкнутой области фокуса внутри заданного контейнера.