В браузере фокус определяет, какой элемент интерфейса получает ввод с клавиатуры или управляется средствами навигации. Управление фокусом критически важно для доступности приложений, так как позволяет пользователям, использующим клавиатуру или вспомогательные технологии, эффективно взаимодействовать с интерфейсом. Библиотека ally.js предоставляет набор инструментов для детального контроля фокуса, включая проверку, установку и управление его поведением.
Фокус в DOM связан с двумя состояниями: активный
элемент (document.activeElement) и
элемент, который может быть фокусирован. Не каждый
элемент может быть естественно фокусируемым; для этого необходимо
использовать атрибуты tabindex или стандартные элементы
формы (<button>, <input>,
<a> с href).
ally.js предоставляет функции для определения, может ли элемент быть фокусируемым:
import * as ally from 'ally.js';
const button = document.querySelector('button');
const isFocusable = ally.is.focusable(button);
const isTabbable = ally.is.tabbable(button);
is.focusable проверяет, можно ли
установить фокус на элемент вообще.is.tabbable проверяет, будет ли
элемент доступен при навигации с помощью клавиши Tab.Эти функции учитывают стили CSS (display: none,
visibility: hidden), атрибут disabled и другие
ограничения, что позволяет точно определить доступность элемента.
Для управления последовательностью фокуса ally.js предлагает методы поиска элементов, по которым можно перемещаться клавишей Tab:
const tabbableElements = ally.query.tabbable({
context: document,
});
query.tabbable возвращает массив
элементов, доступных при последовательной навигации по Tab.query.focusable возвращает все
элементы, на которые возможно установить фокус, включая те, что не
доступны через Tab.Это особенно полезно при создании модальных окон, всплывающих меню или кастомных компонентов, где необходимо контролировать ограниченный набор элементов.
ally.js позволяет устанавливать фокус на любой элемент программно с учётом доступности:
ally.focus.within(button);
ally.focus.firstTabbable(document.querySelector('#modal'));
ally.focus.lastTabbable(document.querySelector('#modal'));
focus.within устанавливает фокус
внутри указанного контейнера.focus.firstTabbable и
focus.lastTabbable позволяют управлять
фокусом в пределах компонента, обеспечивая циклическую навигацию.Такое управление необходимо для соблюдения принципов доступности при работе с модальными окнами и диалогами.
ally.js упрощает слежение за изменениями фокуса:
ally.on.focus(event => {
console.log('Фокус изменился', event.target);
});
Это позволяет реализовать динамическое управление интерфейсом, например, подсветку активного элемента или автоматическое переключение фокуса при закрытии модального окна.
Для сложных интерфейсов важно изолировать фокус внутри определённого контейнера, чтобы пользователи не уходили за пределы модального окна:
const modal = document.querySelector('#modal');
const focusTrap = ally.trap.focus(modal, {
escapeDeactivates: true,
allowOutsideClick: false,
});
trap.focus блокирует фокус внутри
контейнера, перенаправляя навигацию Tab циклически.Focus trap обеспечивает корректное поведение интерфейса и предотвращает потерю фокуса при взаимодействии с модальными компонентами.
При динамическом добавлении элементов на страницу необходимо обновлять список фокусируемых элементов:
const newButton = document.createElement('button');
document.body.appendChild(newButton);
if (ally.is.tabbable(newButton)) {
ally.focus.within(newButton);
}
ally.js учитывает изменения DOM и позволяет корректно обрабатывать новые элементы без дополнительной ручной логики.
Хотя ally.js не занимается CSS напрямую, библиотека может быть использована вместе с классами для визуального отображения состояния фокуса:
:focus-visible {
outline: 2px solid #0078d7;
outline-offset: 2px;
}
:focus-visible обеспечивает видимую
индикацию фокуса только для навигации с клавиатуры, сохраняя чистоту
интерфейса при работе мышью.Эти подходы обеспечивают высокую доступность и удобство навигации по интерфейсу, снижая риск пропуска элементов пользователями клавиатуры или вспомогательных технологий.