Библиотека ally.js предназначена для упрощения работы с доступностью веб-компонентов, особенно когда требуется управлять фокусом и взаимодействием с пользователем через клавиатуру. В контексте модальных окон она решает ключевые задачи: предотвращение потери фокуса, ограничение навигации по скрытому контенту и обеспечение корректного возвращения фокуса после закрытия модального окна.
Для модального окна критически важно, чтобы фокус оставался внутри
него до момента закрытия. ally.js предоставляет метод
trapFocus, который реализует замыкание
фокуса:
import { trapFocus, removeTrapFocus } from 'ally.js';
const modal = document.querySelector('#myModal');
// Активация замыкания фокуса
trapFocus({
context: modal,
initialFocus: modal.querySelector('[autofocus]') || modal,
escapeDeactivates: true
});
// Деактивация при закрытии
removeTrapFocus(modal);
Ключевые моменты:
context — DOM-элемент, внутри которого необходимо
удерживать фокус.initialFocus — элемент, на котором должен оказаться
фокус при открытии модального окна.escapeDeactivates — позволяет закрывать модальное окно
с клавишей Esc.Чтобы пользователь не мог взаимодействовать с элементами страницы за
модальным окном, нужно временно скрывать их из доступности. ally.js
поддерживает атрибут aria-hidden, что
позволяет программно скрыть все остальные элементы страницы:
import { hideOthers } from 'ally.js';
const modal = document.querySelector('#myModal');
const restore = hideOthers(modal);
// restore() снимает скрытие при закрытии модального окна
Особенности:
hideOthers автоматически добавляет
aria-hidden="true" ко всем элементам вне модального
окна.restore необходима для
восстановления исходного состояния DOM после закрытия окна.Для улучшения доступности важно корректно обрабатывать клавиши навигации:
modal.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
closeModal();
}
});
ally.js в сочетании с trapFocus обеспечивает циклическое
перемещение фокуса при нажатии клавиш Tab и Shift+Tab, предотвращая
выход фокуса за пределы модального окна.
Модальные окна часто создаются динамически. ally.js поддерживает динамическое обновление фокусируемых элементов:
trapFocus({
context: modal,
updateOnMutation: true
});
updateOnMutation отслеживает изменения DOM внутри
модального окна.Для модальных окон с множеством элементов важно задать приоритет фокуса:
trapFocus({
context: modal,
initialFocus: modal.querySelector('.first-input'),
fallbackFocus: modal.querySelector('button.close')
});
initialFocus — первичный элемент для фокуса.fallbackFocus — используется, если
initialFocus недоступен, что предотвращает потерю
фокуса.Модальные окна часто имеют анимацию. ally.js учитывает задержку фокуса, позволяя синхронизировать его с CSS-анимациями:
setTimeout(() => {
trapFocus({ context: modal });
}, 300); // 300ms соответствует длительности анимации
import { trapFocus, removeTrapFocus, hideOthers } from 'ally.js';
const modal = document.querySelector('#myModal');
const openButton = document.querySelector('#openModal');
const closeButton = modal.querySelector('.close');
let restoreHidden;
openButton.addEventListener('click', () => {
modal.style.display = 'block';
restoreHidden = hideOthers(modal);
trapFocus({
context: modal,
initialFocus: modal.querySelector('input'),
escapeDeactivates: true
});
});
closeButton.addEventListener('click', () => {
modal.style.display = 'none';
restoreHidden();
removeTrapFocus(modal);
});
Особенности реализации:
aria-hidden.initialFocus или
autofocus, чтобы пользователь сразу попадал на логичный
элемент.hideOthers для предотвращения
взаимодействия с остальной страницей.updateOnMutation для
корректного обновления фокусируемых элементов.trapFocus
с завершением анимации.Такое использование ally.js гарантирует, что модальные окна будут полностью доступны для пользователей, использующих клавиатуру и вспомогательные технологии, а также соответствовать современным стандартам WCAG.