Библиотека Focus-trap предназначена для управления фокусом внутри определённого контейнера, обычно модального окна, панели или всплывающего интерфейса. Основная задача — не допустить выхода фокуса за пределы заданного элемента при навигации с клавиатуры, что обеспечивает доступность интерфейсов.
Ключевые возможности:
Tab и циклическая навигация по
интерактивным элементам.Установка через npm:
npm install 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'),
clickOutsideDeactivates: true,
escapeDeactivates: true
});
Объяснение параметров:
onActivate — функция, вызываемая при активации ловушки
фокуса.onDeactivate — функция при деактивации.clickOutsideDeactivates — возможность закрытия ловушки
кликом вне контейнера.escapeDeactivates — закрытие по клавише
Esc.Активация и деактивация:
document.getElementById('openModal').addEventListener('click', () => focusTrap.activate());
document.getElementById('closeModal').addEventListener('click', () => focusTrap.deactivate());
При использовании анимации важно учитывать задержку появления
элементов. Если активировать ловушку до завершения анимации, фокус может
оказаться на невидимом элементе. Для синхронизации с CSS-анимацией
применяются события transitionend или
animationend.
Пример:
modal.addEventListener('transitionend', () => focusTrap.activate());
modal.classList.add('is-active');
Такой подход гарантирует, что фокус перейдёт только после того, как модальное окно визуально отобразится.
По умолчанию фокус устанавливается на первый интерактивный элемент внутри контейнера. Можно указать конкретный элемент:
const focusTrap = createFocusTrap(modal, {
initialFocus: '#modalInput'
});
initialFocus может быть:
Если в контейнере появляются новые интерактивные элементы после активации ловушки фокуса, Focus-trap автоматически включает их в цикл навигации. Однако иногда требуется ручная синхронизация:
focusTrap.updateTabbableNodes();
Этот метод пересчитывает доступные для фокуса элементы и поддерживает корректную циклическую навигацию.
Для более сложных случаев, когда элементы появляются поэтапно, можно
использовать опцию checkCanFocusTrap:
const focusTrap = createFocusTrap(modal, {
checkCanFocusTrap: (container) => {
return new Promise((resolve) => {
setTimeout(() => resolve(true), 300); // задержка для завершения анимации
});
}
});
Фокус будет активирован только после выполнения промиса, что позволяет дождаться завершения анимации или асинхронной загрузки контента.
Появление модального окна с плавной прозрачностью:
.modal {
opacity: 0;
transform: translateY(-20px);
transition: opacity 0.3s, transform 0.3s;
}
.modal.is-active {
opacity: 1;
transform: translateY(0);
}
openButton.addEventListener('click', () => {
modal.classList.add('is-active');
modal.addEventListener('transitionend', () => focusTrap.activate(), { once: true });
});
Закрытие с анимацией:
closeButton.addEventListener('click', () => {
modal.classList.remove('is-active');
modal.addEventListener('transitionend', () => focusTrap.deactivate(), { once: true });
});
aria-hidden и inert для
элементов вне модального окна, чтобы предотвратить их
взаимодействие.Tab и Shift+Tab
при всех состояниях анимации.Focus-trap легко интегрируется с фреймворками типа React, Vue или Angular через обёртки или хук-решения. В React, например, создаются эффекты, синхронизирующие активацию ловушки с состоянием компонента и CSS-анимациями.
Фокус в интерактивных интерфейсах, особенно при анимации, требует внимательного управления состояниями элементов. Focus-trap предоставляет гибкий набор инструментов для безопасного и предсказуемого контроля фокуса, делая пользовательский опыт более доступным и удобным.