Библиотека Focus-trap предназначена для ограничения фокуса внутри определённого контейнера, обычно модального окна, панели или другого интерактивного блока. Основная цель — предотвратить случайное перемещение фокуса за пределы контейнера, что важно для доступности (a11y) и управляемости интерфейса с клавиатуры.
Иногда стандартного поведения библиотеки недостаточно: не все интерактивные элементы можно автоматически «поймать». В таких случаях используется возможность принудительного включения элементов в trap, что позволяет гарантировать доступность специфических кнопок, ссылок или кастомных элементов управления.
focusTrapOptionsПри инициализации trap через
createFocusTrap(container, options) можно передать объект
options, содержащий ключевые параметры:
import { createFocusTrap } from 'focus-trap';
const trap = createFocusTrap('#modal', {
onActivate: () => console.log('Trap активирован'),
onDeactivate: () => console.log('Trap деактивирован'),
clickOutsideDeactivates: true,
tabbableOptions: {
includeFallback: true
}
});
Важной частью для принудительного включения является опция
allowOutsideClick и tabbableOptions. Однако
для явного контроля элементов используется метод
setInitialFocus и опция fallbackFocus
совместно с фильтрацией элементов через
tabbableOptions.
tabbableOptions.includeБиблиотека Focus-trap интегрирует модуль
tabbable, который определяет интерактивные элементы. В
tabbableOptions можно указать селекторы или функции для
расширения списка «ловимых» элементов.
const trap = createFocusTrap('#modal', {
tabbableOptions: {
include: ['.custom-button', '#special-link']
}
});
Здесь .custom-button и #special-link будут
всегда включены в список элементов, на которые можно перейти клавишей
Tab, даже если стандартная фильтрация их не учитывает.
Особенности работы include:
role="button" без
tabindex).Если элементы создаются динамически после активации trap, можно передавать функцию:
const trap = createFocusTrap('#modal', {
tabbableOptions: {
include: () => Array.from(document.querySelectorAll('.dynamic-focus'))
}
});
Каждый раз при активации trap функция вызывается, возвращая актуальный список элементов. Это позволяет управлять фокусом даже в сложных интерфейсах с асинхронной подгрузкой контента.
Для активации trap с фокусом на заранее выбранном элементе
применяется опция setInitialFocus:
const trap = createFocusTrap('#modal', {
setInitialFocus: '#first-input'
});
Даже если #first-input обычно не tabbable, он будет
включен в trap и получит фокус при активации.
Аналогично работает fallbackFocus — элемент, на который
фокус перейдёт, если tabbable-элементы отсутствуют:
fallbackFocus: '#default-focus'
tabindex=0.role="link" без тега <a>.import { createFocusTrap } from 'focus-trap';
const modalTrap = createFocusTrap('#modal', {
onActivate: () => console.log('Modal trap активирован'),
setInitialFocus: () => document.querySelector('#important-button'),
tabbableOptions: {
include: ['.always-focus', (container) => container.querySelectorAll('[data-dynamic-focus]')]
},
clickOutsideDeactivates: true
});
// Активация trap
document.querySelector('#open-modal').addEventListener('click', () => modalTrap.activate());
В этом примере:
#important-button получает фокус при открытии
модалки..always-focus и атрибутом
data-dynamic-focus включаются в список tabbable.Принудительное включение элементов позволяет максимально гибко управлять фокусом, делая интерфейсы доступными и удобными для навигации с клавиатуры даже в сложных приложениях.