Библиотека Focus-trap предназначена для управления фокусом в веб-приложениях. Основная задача — ограничить перемещение фокуса клавиатуры внутри определённого контейнера. Это критично для модальных окон, всплывающих подсказок и других интерфейсных элементов, где важно, чтобы пользователь оставался внутри интерактивного блока до завершения действия.
Фокусировка обеспечивается с помощью методов activate()
и deactivate(). При активации ловушки фокус автоматически
переводится на первый доступный элемент, а при деактивации возвращается
к элементу, который имел фокус до активации.
import { createFocusTrap } from 'focus-trap';
const modal = document.getElementById('modal');
const trap = createFocusTrap(modal);
trap.activate();
// Для выхода из ловушки
trap.deactivate();
В сложных интерфейсах возникает необходимость одновременного существования нескольких ловушек фокуса. Например, модальное окно может содержать всплывающую подсказку, которая также ограничивает фокус. Неправильная организация ловушек приводит к конфликтам: фокус “залипает” в одном из контейнеров или некорректно возвращается после закрытия.
Вложенная ловушка — это когда внутри активной ловушки создаётся новая. Основные правила:
Деактивация внешней ловушки при активации внутренней. Это предотвращает потерю контроля над фокусом: только внутренняя ловушка принимает события клавиатуры.
Возврат к внешней ловушке после закрытия внутренней. При деактивации внутренней ловушки необходимо повторно активировать внешнюю.
Пример:
const outerTrap = createFocusTrap(document.getElementById('modalOuter'), {
onDeactivate: () => console.log('Внешняя ловушка деактивирована')
});
const innerTrap = createFocusTrap(document.getElementById('modalInner'), {
onActivate: () => {
outerTrap.deactivate();
},
onDeactivate: () => {
outerTrap.activate();
}
});
// Активация внешней ловушки
outerTrap.activate();
// Активация внутренней
innerTrap.activate();
Ключевой момент: порядок активации и деактивации должен строго соблюдаться, иначе фокус может потеряться.
Для интерфейсов с несколькими независимыми модальными окнами требуется управление стеком активных ловушек. Один из подходов — хранение массива активных ловушек и переключение между ними:
const trapsStack = [];
function activateTrap(trap) {
if (trapsStack.length > 0) {
trapsStack[trapsStack.length - 1].deactivate();
}
trap.activate();
trapsStack.push(trap);
}
function deactivateTrap() {
const trap = trapsStack.pop();
trap.deactivate();
if (trapsStack.length > 0) {
trapsStack[trapsStack.length - 1].activate();
}
}
Этот подход обеспечивает:
Focus-trap поддерживает опцию clickOutsideDeactivates. В
сложных композициях с несколькими ловушками важно настроить её
правильно, чтобы клики на фоне не деактивировали внешние ловушки
преждевременно.
const trap = createFocusTrap(modal, {
clickOutsideDeactivates: true,
escapeDeactivates: true
});
Рекомендация: Для вложенных ловушек внешняя ловушка должна не реагировать на клики вне контейнера, пока активна внутренняя. Это предотвращает случайное закрытие внешнего модального окна.
Для более сложных сценариев можно использовать методы
pause() и unpause(). Это особенно актуально
при динамическом создании элементов внутри активной ловушки:
trap.pause(); // временно приостанавливает ловушку
// манипуляции с DOM
trap.unpause(); // возобновляет управление фокусом
Такой подход позволяет:
pause() для динамических изменений
DOM внутри активной ловушки.clickOutsideDeactivates и
escapeDeactivates индивидуально для каждого
уровня, чтобы избежать конфликтов.Эти правила обеспечивают стабильное и предсказуемое поведение фокуса даже в многоуровневых интерфейсах.