Библиотека ally.js предназначена для управления доступностью веб-интерфейсов, обеспечивая контроль фокуса и навигации для пользователей с ограниченными возможностями. Она предоставляет высокоуровневые API для работы с фокусом, проверкой видимости элементов, управлением клавиатурной навигацией и обработкой атрибутов ARIA. В основе работы лежат функции, позволяющие безопасно определять доступные элементы и программно управлять их активацией.
Ключевой подход заключается в изолировании элементов интерфейса, которые должны быть доступны в текущем контексте, и блокировании взаимодействия с остальными частями страницы. Это особенно важно при реализации модальных окон, всплывающих меню или других компонент, где фокус должен оставаться внутри определённой области.
ally.js предоставляет функции для поиска элементов, доступных для фокуса:
import ally from 'ally.js';
const focusable = ally.query.tabable({ context: document.body });
Для перемещения фокуса используется функция focus():
ally.focus.focus({
context: document.getElementById('modal'),
strategy: 'first' // 'first', 'last', или конкретный элемент
});
Иногда необходимо перемещать фокус не по порядку DOM, а по визуальному расположению элементов. ally.js поддерживает вычисление видимых и активных элементов:
const visibleElements = ally.query.focusable({
context: document.body,
includeOnlyTabbable: true,
filter: function(element) {
return element.getBoundingClientRect().top < window.innerHeight;
}
});
Для компонентов с динамическим контентом важно учитывать ARIA-атрибуты, которые указывают состояние элементов:
const activeTab = ally.query.firstTabbable({
context: document.getElementById('tabs'),
filter: function(element) {
return element.getAttribute('aria-selected') === 'true';
}
});
ally.focus.focus({ element: activeTab });
Для поддержания логики доступности важно возвращать фокус к элементу, с которого он был изначально переведён:
const trigger = document.getElementById('openModal');
const modal = document.getElementById('modal');
trigger.addEventListener('click', () => {
ally.focus.focus({ context: modal, strategy: 'first' });
});
modal.addEventListener('close', () => {
ally.focus.focus({ element: trigger });
});
ally.js предоставляет методы для обхода элементов DOM с учётом доступности:
const current = document.activeElement;
const next = ally.query.nextTabbable({ context: document.body, start: current });
ally.focus.focus({ element: next });
ally.js умеет работать с элементами, которые появляются или исчезают после загрузки страницы. Для этого используется динамический пересчёт фокусируемых элементов:
const observer = new MutationObserver(() => {
const focusable = ally.query.tabable({ context: modal });
// можно обновлять внутреннюю логику навигации
});
observer.observe(modal, { childList: true, subtree: true });
В сложных интерфейсах можно комбинировать методы:
focusable с фильтром по координатам).Такой подход обеспечивает полную предсказуемость и контроль над навигацией, улучшая доступность приложений для всех категорий пользователей.
Для предотвращения выхода фокуса за пределы активного компонента (например, модального окна) используется focus trap:
ally.maintain.disabled({
context: document.body,
filter: function(element) {
return !modal.contains(element);
}
});
Эти методы позволяют строить интерактивные, доступные интерфейсы без глубокого вмешательства в нативные события браузера, минимизируя ошибки и сохраняя совместимость с экранными читалками и клавиатурной навигацией.