Программное управление фокусом

Библиотека ally.js предоставляет инструменты для программного управления фокусом в веб-приложениях, обеспечивая доступность интерфейса и корректную навигацию для пользователей с различными устройствами ввода. Фокус в DOM может быть управляем как программно, так и через взаимодействие пользователя с клавиатурой или мышью. Ally.js позволяет выявлять фокусируемые элементы, фильтровать их по доступности и управлять переходами между ними.

Ключевое понятие — фокусируемый элемент. Это любой элемент, на который может быть установлен фокус через tabindex, встроенные интерактивные элементы (<button>, <a>, <input> и т. д.) или кастомные компоненты с использованием скриптов. Ally.js предоставляет API для работы с такими элементами с учётом их состояния (disabled, hidden, aria-hidden и т. д.).


Получение и фильтрация фокусируемых элементов

Метод ally.query.focusable() позволяет получить список элементов, которые можно сфокусировать в текущем документе:

const focusableElements = ally.query.focusable({ context: document.body });
console.log(focusableElements);

Параметры метода:

  • context — DOM-узел, в пределах которого выполняется поиск.
  • includeOnlyTabbable — если true, возвращаются только элементы, доступные через клавишу Tab.
  • filter — функция для дополнительной фильтрации элементов.

Пример фильтрации по CSS-классу:

const buttons = ally.query.focusable({
  context: document.body,
  filter: node => node.classList.contains('interactive-button')
});

Такой подход позволяет строить точный список элементов для управления фокусом.


Программная установка фокуса

Для установки фокуса используется метод ally.focus.element():

ally.focus.element(document.getElementById('submit-btn'));

Особенности:

  • Поддерживается установка фокуса на элементы, скрытые через display: none или visibility: hidden, при использовании опции preventScroll.
  • Можно использовать параметр silent для подавления выбрасываемых исключений при попытке фокусировки недоступного элемента:
ally.focus.element(document.querySelector('.hidden-input'), { silent: true });

Метод возвращает промис, который разрешается после завершения установки фокуса, что важно для асинхронных интерфейсов и анимаций.


Управление порядком фокуса

Ally.js предоставляет средства для контроля порядка перехода по Tab. Метод ally.focus.next() позволяет программно перейти к следующему фокусируемому элементу:

ally.focus.next(document.activeElement);

Параметры:

  • context — ограничивает поиск элементов.
  • cycle — при true фокус вернётся к первому элементу после последнего.
  • includeOnlyTabbable — аналогично фильтру выше.

Методы ally.focus.previous() и ally.focus.first() работают аналогично, обеспечивая полное управление навигацией.


Создание ограниченных областей фокуса

Для модальных окон или всплывающих панелей важно ограничить фокус внутри контейнера. Ally.js предоставляет функцию ally.trap.focus():

const trap = ally.trap.focus({
  context: document.getElementById('modal'),
  cycle: true
});

Особенности работы:

  • Фокус автоматически перемещается между элементами внутри указанного контейнера.
  • Параметр cycle позволяет зациклить фокус.
  • Функция возвращает объект с методом disengage() для снятия ограничения:
trap.disengage();

Такой подход предотвращает потерю фокуса за пределами модального окна и улучшает доступность интерфейса.


Обработка динамически добавляемых элементов

Ally.js учитывает элементы, которые появляются или исчезают динамически. Для корректной работы с такими элементами следует:

  1. Запрашивать фокусируемые элементы после вставки в DOM.
  2. Использовать промисы для асинхронной установки фокуса.
  3. Применять фильтры, чтобы исключить скрытые или недоступные элементы.

Пример:

document.body.appendChild(newButton);
ally.focus.element(newButton).then(() => {
  console.log('Фокус установлен на динамически добавленную кнопку');
});

Взаимодействие с ARIA-атрибутами

Ally.js учитывает атрибуты ARIA при фильтрации:

  • aria-hidden=“true” — элемент не фокусируется.
  • role=“dialog” — помогает корректно ограничить фокус.
  • aria-disabled=“true” — элемент считается недоступным для фокуса.

Это позволяет строить интерфейсы, соответствующие стандартам WCAG, без дополнительных проверок.


Практическое использование

Типичные сценарии применения ally.js:

  1. Модальные окна — trap.focus для замыкания фокуса внутри модалки.
  2. Списки и таблицы — focus.next/focus.previous для навигации с клавиатуры.
  3. Динамические формы — программная установка фокуса на первый невалидный элемент.
  4. Компоненты с кастомной интерактивностью — фильтрация focusable элементов для корректного таб-циклинга.

Рекомендации по интеграции

  • Всегда использовать filter для исключения невидимых элементов.
  • Для асинхронного контента применять promises при фокусировке.
  • Комбинировать trap.focus и focus.next/previous для сложных интерфейсов.
  • Проверять поддержку браузеров и fallback на стандартные методы element.focus() при необходимости.

Ally.js предоставляет гибкую систему для программного управления фокусом, делая интерфейсы доступными и предсказуемыми, упрощая реализацию сложной логики навигации без ручного обхода DOM.