Библиотека focus-trap предназначена для управления
фокусом внутри определённого DOM-элемента. Она особенно полезна при
создании модальных окон, всплывающих панелей и любых интерфейсов, где
важно, чтобы пользователь не покидал определённый контейнер с
клавиатуры.
Фокусировка реализуется через замыкание клавиатурного
фокуса. Когда focus-trap активен, нажатие клавиши
Tab или Shift+Tab ограничивает перемещение фокуса элементами внутри
контейнера.
Создание простого трапа выглядит так:
import { createFocusTrap } from 'focus-trap';
const modal = document.getElementById('modal');
const trap = createFocusTrap(modal, {
onActivate: () => modal.classList.add('active'),
onDeactivate: () => modal.classList.remove('active'),
});
trap.activate();
Ключевые параметры:
onActivate — вызывается при активации фокус-трапа.onDeactivate — вызывается при деактивации.initialFocus — элемент, который получит фокус при
активации.allowOutsideClick — разрешает клики вне
контейнера.escapeDeactivates — позволяет закрывать трап по
Esc.Сценарии, когда контейнер фокусировки меняется во время работы
приложения, встречаются часто: динамически подгружаемые модальные окна,
смена вкладок, замена содержимого панели. focus-trap не
поддерживает прямую смену контейнера после создания трапа. Решение
состоит в деактивации старого трапа и создании
нового.
let currentTrap = null;
function activateTrap(element) {
if (currentTrap) currentTrap.deactivate();
currentTrap = createFocusTrap(element, { escapeDeactivates: true });
currentTrap.activate();
}
const modalA = document.getElementById('modalA');
const modalB = document.getElementById('modalB');
activateTrap(modalA);
// Позже требуется переключиться
activateTrap(modalB);
Такой подход гарантирует, что фокус всегда контролируется актуальным контейнером, и предотвращает конфликт нескольких активных трапов.
Если содержимое контейнера создаётся асинхронно,
focus-trap необходимо активировать после того, как элементы
будут добавлены в DOM.
const modal = document.getElementById('modal');
async function loadContent() {
const response = await fetch('/modal-content.html');
modal.innerHTML = await response.text();
activateTrap(modal);
}
Использование initialFocus особенно важно для
динамических элементов, чтобы фокус устанавливался на конкретный
первый интерактивный элемент, а не просто на контейнер.
activateTrap(modal, {
initialFocus: () => modal.querySelector('input:first-of-type')
});
Иногда внутри контейнера меняются элементы, и требуется обновить
список фокусируемых элементов. Для этого применяют метод
deactivate с последующей активацией нового трапа на том же
контейнере:
function refreshTrap(container) {
if (currentTrap) currentTrap.deactivate({ returnFocus: false });
currentTrap = createFocusTrap(container);
currentTrap.activate();
}
Параметр returnFocus: false предотвращает возврат фокуса
к предыдущему элементу, что актуально при смене содержимого без закрытия
модального окна.
В интерфейсах с несколькими модальными окнами важно, чтобы активным оставался только один трап.
function switchTrap(from, to) {
if (currentTrap) currentTrap.deactivate();
currentTrap = createFocusTrap(to, {
onDeactivate: () => from.focus()
});
currentTrap.activate();
}
Использование onDeactivate позволяет возвращать фокус на
предыдущий контейнер при закрытии модального окна, создавая плавный
пользовательский опыт.
activate().initialFocus. Это
предотвращает потерю фокуса или случайное выделение неинтерактивных
элементов.deactivate и activate. Это
универсальная схема для всех сценариев, где DOM контейнера
меняется.Динамическое управление контейнером в focus-trap
позволяет создавать сложные интерфейсы с множеством модальных окон,
вкладок или асинхронным контентом, сохраняя полное управление фокусом и
предсказуемое поведение клавиатурной навигации.