Библиотека Focus-trap предназначена для управления фокусом в веб-приложениях, особенно в интерактивных компонентах, таких как модальные окна, панели навигации, всплывающие меню и диалоговые элементы. Она обеспечивает контейнерный фокус, предотвращая выход фокуса за пределы заданного элемента, что критично для доступности (accessibility) и удобства взаимодействия с клавиатурой.
Принцип работы: Focus-trap создаёт виртуальный
«замкнутый цикл» фокуса внутри элемента. Когда пользователь перемещается
по интерактивным элементам с помощью клавиши Tab или
Shift + Tab, фокус не покидает контейнер, а при достижении
последнего или первого элемента возвращается к началу или концу списка
соответственно.
Библиотека распространяется через NPM и поддерживает стандартные модули ES6.
npm install focus-trap
Подключение через ES-модуль:
import { createFocusTrap } from 'focus-trap';
Также доступна UMD-версия для использования через тег
<script>:
<script src="https://unpkg.com/focus-trap/dist/focus-trap.min.js"></script>
Для активации фокус-трапа необходимо определить контейнер и вызвать
createFocusTrap:
const modal = document.getElementById('modal');
const focusTrap = createFocusTrap(modal, {
onActivate: () => modal.classList.add('active'),
onDeactivate: () => modal.classList.remove('active'),
clickOutsideDeactivates: true,
escapeDeactivates: true,
initialFocus: '#modal input:first-of-type'
});
Ключевые параметры:
onActivate и onDeactivate – функции,
вызываемые при активации и деактивации фокус-трапа.clickOutsideDeactivates – фокус-трап будет
деактивирован при клике вне контейнера.escapeDeactivates – деактивация при нажатии клавиши
Esc.initialFocus – определяет элемент, который получит
фокус при активации.Focus-trap предоставляет основные методы для управления состоянием:
activate() – активирует фокус-трап.deactivate() – деактивирует фокус-трап, возвращая фокус
на исходный элемент.pause() – временно приостанавливает работу
фокус-трапа.unpause() – возобновляет работу после паузы.Пример использования с модальным окном:
document.getElementById('openModal').addEventListener('click', () => {
focusTrap.activate();
});
document.getElementById('closeModal').addEventListener('click', () => {
focusTrap.deactivate();
});
Focus-trap позволяет тонко настраивать поведение:
returnFocusOnDeactivate (по умолчанию
true) – возвращает фокус на элемент, который был активен до
активации трапа.fallbackFocus – элемент, который получает фокус, если
initialFocus не найден.allowOutsideClick – разрешает клики вне контейнера без
деактивации.tabbableOptions – объект для настройки поиска
элементов, доступных для фокусировки (например,
displayCheck: 'none' игнорирует скрытые элементы).Пример расширенной конфигурации:
const focusTrap = createFocusTrap(modal, {
initialFocus: '#modal input',
fallbackFocus: modal,
clickOutsideDeactivates: false,
allowOutsideClick: true,
tabbableOptions: { displayCheck: 'full' }
});
При использовании веб-компонентов важно учитывать Shadow DOM, так как стандартный поиск интерактивных элементов внутри Shadow DOM не всегда работает автоматически. Focus-trap поддерживает передачу tabbable elements вручную:
import { tabbable } from 'focus-trap';
class MyModal extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<div id="container">
<input type="text" />
<button id="close">Close</button>
</div>
`;
const focusTrap = createFocusTrap(shadow.getElementById('container'), {
initialFocus: shadow.querySelector('input'),
onDeactivate: () => console.log('Trap deactivated')
});
shadow.getElementById('close').addEventListener('click', () => focusTrap.deactivate());
focusTrap.activate();
}
}
customElements.define('my-modal', MyModal);
Особенности при работе с Shadow DOM:
initialFocus).tabbable() можно использовать для получения всех
интерактивных элементов внутри Shadow DOM.Focus-trap корректно обрабатывает динамически добавляемые элементы. Если внутри контейнера появляются новые интерактивные элементы после активации трапа, они автоматически включаются в цикл фокуса при стандартной конфигурации.
Пример с динамическим списком:
const listContainer = document.getElementById('list');
const focusTrap = createFocusTrap(listContainer);
focusTrap.activate();
document.getElementById('addItem').addEventListener('click', () => {
const newItem = document.createElement('button');
newItem.textContent = 'New Item';
listContainer.appendChild(newItem);
});
Focus-трап автоматически учитывает добавленные кнопки при навигации
через Tab.
Focus-trap улучшает accessibility:
Esc и кликами вне контейнера
повышает удобство для пользователей с ассистивными технологиями.Для соблюдения ARIA-стандартов рекомендуется использовать
aria-modal="true" на контейнерах, где активен фокус-трап, а
также управлять видимостью элементов за пределами контейнера с помощью
aria-hidden.
initialFocus или
fallbackFocus, чтобы предотвратить потерю фокуса.tabbable() для
кастомного управления.Focus-trap является надёжным инструментом для организации корректного управления фокусом, упрощая разработку доступных и интерактивных интерфейсов в современном вебе.