В интерфейсах, построенных на веб-технологиях, управление фокусом является важной частью доступности. Фокус определяет элемент, который в текущий момент принимает ввод с клавиатуры. Браузеры автоматически перемещают его при навигации с помощью клавиши Tab, при клике мышью или при программном управлении через JavaScript.
Однако с точки зрения доступности важен не только сам факт получения фокуса элементом, но и источник этого фокуса — способ, которым пользователь взаимодействовал со страницей. Один и тот же элемент может получить фокус:
element.focus().Различение этих сценариев необходимо для корректного поведения интерфейса. Например:
Библиотека ally.js предоставляет инструменты для определения источника фокуса и управления поведением интерфейса в зависимости от типа взаимодействия.
Стандартный DOM-API не предоставляет прямого способа определить,
почему именно элемент получил фокус. Событие
focus не содержит информации о предшествующем действии
пользователя.
Например:
input.addEventListener('focus', function() {
console.log('Элемент получил фокус');
});
В обработчике невозможно понять, произошёл ли фокус из-за:
Попытки решить задачу через прослушивание отдельных событий
(keydown, mousedown, touchstart)
приводят к ряду проблем:
В результате появляется необходимость в механизме, который:
Эта задача решается модулем ally.js, отвечающим за определение источника фокуса.
В библиотеке используется подход, основанный на глобальном
отслеживании пользовательского ввода. Фиксируются события,
которые могут привести к перемещению фокуса, после чего информация
используется при обработке события focus.
Основные категории взаимодействия:
| Тип источника | Описание |
|---|---|
keyboard |
фокус появился после клавиатурного действия |
mouse |
фокус появился после клика мышью |
touch |
фокус получен после касания |
script |
фокус установлен программно |
Для определения источника ally.js отслеживает несколько ключевых событий:
keydownmousedownpointerdowntouchstartfocusАлгоритм работы выглядит следующим образом:
focus анализируется контекст
предыдущего события.Такой подход обеспечивает более точное определение причины получения фокуса.
Для определения источника взаимодействия используется модуль:
ally.interactionType
Этот модуль регистрирует глобальные слушатели событий и определяет текущий тип взаимодействия пользователя.
Подключение модуля:
import interactionType from 'ally.js/interaction-type';
После инициализации модуль начинает отслеживать пользовательские действия и поддерживает актуальное состояние источника взаимодействия.
Получение текущего типа взаимодействия:
interactionType.get();
Пример результата:
"keyboard"
или
"mouse"
Возможные значения:
keyboardmousetouchpointerscriptЭто значение отражает последний способ взаимодействия пользователя со страницей.
Рассмотрим ситуацию, когда необходимо показывать визуальный индикатор фокуса только при клавиатурной навигации.
import interactionType from 'ally.js/interaction-type';
document.addEventListener('focus', function(event) {
if (interactionType.get() === 'keyboard') {
event.target.classList.add('focus-visible');
}
}, true);
CSS-стиль:
.focus-visible {
outline: 3px solid #4A90E2;
}
В результате:
Этот паттерн широко применяется для улучшения пользовательского опыта без ущерба доступности.
Модуль позволяет подписаться на изменение типа взаимодействия. Это полезно для глобального управления состоянием интерфейса.
Пример:
interactionType.onChange(function(type) {
console.log('Тип взаимодействия:', type);
});
При переключении между устройствами ввода в консоли появятся сообщения:
Тип взаимодействия: keyboard
Тип взаимодействия: mouse
Тип взаимодействия: touch
Это позволяет динамически изменять поведение интерфейса.
Отдельную категорию составляет фокус, установленный программно:
element.focus();
В обычном DOM API невозможно отличить такой сценарий от пользовательского. ally.js определяет его через анализ цепочки событий.
Пример:
button.addEventListener('click', function() {
input.focus();
});
В этом случае источник фокуса будет определён как:
script
Это важно для компонентов интерфейса, которые автоматически перемещают фокус.
Например:
При открытии модального окна рекомендуется переводить фокус на первый интерактивный элемент.
Пример:
function openDialog(dialog) {
dialog.classList.add('open');
const focusable = dialog.querySelector('button, input, a');
focusable.focus();
}
Однако в некоторых случаях требуется различать:
С использованием ally.js:
import interactionType from 'ally.js/interaction-type';
function openDialog(dialog) {
dialog.classList.add('open');
const focusable = dialog.querySelector('button, input, a');
if (interactionType.get() === 'keyboard') {
focusable.focus();
}
}
Это позволяет избежать неожиданного изменения фокуса при использовании мыши.
Иногда требуется адаптировать стили интерфейса в зависимости от типа ввода. Например, интерфейсы для сенсорных экранов могут использовать увеличенные области взаимодействия.
Определение текущего типа взаимодействия:
const type = interactionType.get();
Применение CSS-класса:
document.body.classList.add('interaction-' + type);
CSS-пример:
.interaction-touch button {
padding: 16px 20px;
}
.interaction-mouse button {
padding: 8px 12px;
}
Это позволяет динамически адаптировать интерфейс под текущий способ взаимодействия.
Современные устройства могут поддерживать несколько способов ввода одновременно:
Поэтому ally.js не фиксирует один постоянный тип взаимодействия, а отслеживает последний использованный способ ввода.
Пример последовательности действий пользователя:
keyboardmousetouchКаждое новое действие обновляет текущее состояние.
Использование специализированного механизма определения источника фокуса обеспечивает несколько преимуществ.
Кроссбраузерная стабильность
Библиотека учитывает различия в порядке событий и особенностях реализации DOM в разных браузерах.
Поддержка различных устройств ввода
Обрабатываются:
Интеграция с системой управления фокусом
Механизм может использоваться совместно с другими модулями ally.js:
maintain/tab-focusmaintain/disabledquery/focusableУлучшение доступности
Интерфейс корректно реагирует на клавиатурную навигацию, что особенно важно для пользователей:
Несмотря на преимущества, следует учитывать ряд особенностей.
1. Невозможно определить источник фокуса со стопроцентной точностью
В некоторых сценариях события могут перекрываться или происходить почти одновременно.
2. Некоторые браузеры генерируют дополнительные события
Например, сенсорные устройства могут создавать как
touch, так и mouse события.
3. Программные действия могут маскироваться под пользовательские
Если скрипт вызывается внутри обработчика клика, источник может
восприниматься как mouse.
Библиотека использует внутренние эвристики для минимизации подобных проблем.
В крупных интерфейсах определение источника фокуса является частью более широкой системы управления доступностью. Эта система обычно включает:
Модуль определения источника взаимодействия служит фундаментом для этих механизмов, поскольку позволяет интерфейсу реагировать на намерения пользователя, а не только на технический факт получения фокуса.