Focus-trap — это JavaScript-библиотека, предназначенная
для ограничения фокуса клавиатуры внутри определённого DOM-элемента. Это
особенно важно для модальных окон, диалогов и всплывающих меню, где
необходимо предотвратить случайное перемещение фокуса за пределы
интерактивной области.
Библиотека предоставляет интерфейс для создания ловушки фокуса и управления его поведением:
import { createFocusTrap } from 'focus-trap';
const modal = document.getElementById('modal');
const trap = createFocusTrap(modal, {
escapeDeactivates: true,
clickOutsideDeactivates: true
});
trap.activate();
Ключевые параметры конфигурации:
escapeDeactivates — отключает ловушку при нажатии
клавиши Escape.clickOutsideDeactivates — отключает ловушку при клике
вне контейнера.initialFocus — элемент, на котором будет установлен
фокус при активации.fallbackFocus — элемент для фокуса, если
initialFocus недоступен.При работе с массивными структурами DOM стандартные методы поиска
интерактивных элементов (querySelectorAll,
tabindex) могут вызывать значительные задержки. Для крупных
приложений это критично, поэтому focus-trap предлагает
методы оптимизации:
Ограничение области поиска элементов
Вместо сканирования всего документа фокус-ловушка должна работать только с конкретным контейнером:
const trap = createFocusTrap(document.getElementById('modal'), {
tabbableOptions: { container: document.getElementById('modal') }
});
Это предотвращает перебор всех элементов страницы.
Использование кэширования интерактивных элементов
Для больших списков или сложных модальных окон повторный поиск интерактивных элементов не нужен. Можно кэшировать элементы при активации ловушки:
let tabbableElementsCache = [];
const trap = createFocusTrap(modal, {
onActivate: () => {
tabbableElementsCache = Array.from(modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])'));
},
get tabbableElements() {
return tabbableElementsCache;
}
});
Такой подход уменьшает количество операций поиска и ускоряет обработку событий клавиатуры.
Отложенная активация
Если модальное окно или панель содержат тяжёлый контент, активацию ловушки фокуса можно отложить до полной загрузки DOM внутри контейнера:
window.requestAnimationFrame(() => {
trap.activate();
});
Это позволяет браузеру завершить отрисовку элементов и предотвратить мерцания фокуса.
Минимизация подписки на события
По умолчанию focus-trap отслеживает события
keydown, focusin и mousedown. Для
больших деревьев DOM количество таких обработчиков может быть критично.
Рекомендуется отключать ненужные опции:
const trap = createFocusTrap(modal, {
escapeDeactivates: true,
clickOutsideDeactivates: false
});
Если клики вне модального окна не должны деактивировать ловушку, это снижает нагрузку на обработку событий.
Для интерфейсов, где элементы добавляются или удаляются динамически,
необходимо обновлять список tabbable-элементов. В
focus-trap это делается через
updateTabbables():
trap.updateTabbables();
В больших DOM-деревьях лучше вызывать обновление только при изменении контейнера, а не при каждом событии, чтобы избежать лишних вычислений.
Если в приложении одновременно могут существовать несколько модальных окон, важно управлять стеком активных ловушек:
Пример управления стеком:
const traps = [];
function openModal(modal) {
const trap = createFocusTrap(modal);
traps.push(trap);
trap.activate();
}
function closeModal() {
const trap = traps.pop();
trap.deactivate();
if (traps.length) {
traps[traps.length - 1].activate();
}
}
document.querySelectorAll) в больших контейнерах.tabbable опцию с
конкретным контейнером для ускорения поиска интерактивных
элементов.Эти подходы позволяют focus-trap работать эффективно
даже на страницах с тысячами элементов, обеспечивая корректное
управление фокусом и плавную навигацию с клавиатуры.