Трапинг фокуса (focus trapping) — это техника управления фокусом внутри интерактивного элемента, такого как модальное окно, диалог или всплывающее меню. Основная цель — ограничить навигацию с клавиатуры элементами внутри контейнера до момента его закрытия, предотвращая переход фокуса на элементы страницы за пределами контейнера. В JavaScript это критично для обеспечения доступности (accessibility) интерфейсов.
В DOM фокус определяется с помощью свойства
document.activeElement. Все элементы с атрибутом
tabindex или интерактивные элементы (button,
input, a[href]) могут получать фокус. Для
корректного трапинга необходимо отслеживать:
Tab для циклической навигации.Библиотека ally.js предоставляет удобные API для безопасного и кроссбраузерного управления фокусом, включая функции поиска фокусируемых элементов и реализации трапинга.
tabbable(container) Возвращает
массив всех элементов внутри container, которые могут быть
достигнуты с помощью клавиши Tab. Элементы фильтруются по видимости и
атрибуту tabindex.
import { tabbable } from 'ally.js';
const modal = document.querySelector('.modal');
const focusableElements = tabbable(modal);
Ключевой момент: порядок элементов в массиве соответствует порядку обхода Tab.
focusFirstTabbable(container) и
focusLastTabbable(container) Автоматически
устанавливают фокус на первый или последний фокусируемый элемент внутри
контейнера. Это удобно при открытии модального окна.
import { focusFirstTabbable } from 'ally.js';
focusFirstTabbable(modal);focusWithin(container) Проверяет,
находится ли текущий фокус внутри контейнера. Используется для
предотвращения выхода фокуса за пределы модального окна.
import { focusWithin } from 'ally.js';
if (!focusWithin(modal)) {
focusFirstTabbable(modal);
}Для полноценного трапинга необходима обработка события
keydown, отслеживающего клавишу Tab. Основная логика:
Пример с использованием ally.js:
import { tabbable } from 'ally.js';
const modal = document.querySelector('.modal');
function trapFocus(event) {
const focusable = tabbable(modal);
const firstElement = focusable[0];
const lastElement = focusable[focusable.length - 1];
if (event.key === 'Tab') {
if (event.shiftKey) {
if (document.activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
event.preventDefault();
firstElement.focus();
}
}
}
}
modal.addEventListener('keydown', trapFocus);
Особенности: ally.js исключает невидимые или отключённые
элементы, поэтому массив focusable всегда корректен.
После закрытия модального окна важно вернуть фокус на элемент, который инициировал открытие диалога. ally.js не управляет этим напрямую, но можно реализовать вручную:
const trigger = document.querySelector('#open-modal');
trigger.addEventListener('click', () => {
modal.style.display = 'block';
focusFirstTabbable(modal);
});
function closeModal() {
modal.style.display = 'none';
trigger.focus();
}
Ключевой момент: это улучшает доступность для пользователей клавиатуры и экранных читалок.
Иногда фокусируемые элементы добавляются в контейнер динамически.
ally.js позволяет получать актуальный список через
tabbable(container) при каждом событии:
modal.addEventListener('keydown', (event) => {
const focusable = tabbable(modal); // всегда актуально
});
Это предотвращает потерю фокуса, если элементы создаются или удаляются после открытия диалога.
nextFocus(element) /
previousFocus(element) Возвращают ближайший
элемент для навигации, что удобно при сложных интерфейсах с вложенными
контейнерами.
focus(element) Безопасно
устанавливает фокус на элемент, даже если он временно невидим или скрыт
через CSS. Полезно для анимаций открытия модального окна.
isFocusable(element) Проверяет,
может ли элемент принимать фокус, включая скрытые и disabled
элементы.
tabindex без ally.js, так
как порядок обхода Tab может быть нарушен из-за динамических элементов
или CSS-свойства display:none.Использование ally.js делает трапинг фокуса надежным, предотвращает “выход фокуса” из диалогов и обеспечивает соответствие стандартам доступности WCAG. Это особенно важно для сложных веб-приложений с модальными окнами, всплывающими меню и интерактивными компонентами.