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

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

В основе ally.js лежат три ключевых направления:

  1. Определение фокусируемых элементов (tabbable и focusable).
  2. Управление фокусом по событию (focus, blur).
  3. Контроль перехода фокуса при изменении маршрута или состояния интерфейса.

Фокусируемые элементы: focusable и tabbable

ally.js предоставляет функции для выявления элементов, которые можно фокусировать:

import * as ally from 'ally.js';

// Получение всех фокусируемых элементов внутри контейнера
const focusableElements = ally.query.focusable({
  context: document.getElementById('modal')
});

// Получение только тех элементов, которые можно достичь с помощью Tab
const tabbableElements = ally.query.tabbable({
  context: document.getElementById('modal')
});

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

  • Focusable — любой элемент, на который можно установить фокус, включая скрытые или disabled элементы.
  • Tabbable — элементы, которые можно достичь клавишей Tab, игнорируя скрытые или заблокированные.

Правильное различие между этими типами критично при динамическом управлении интерфейсом: для модальных окон обычно используются только tabbable-элементы, чтобы фокус не переходил на невидимые части страницы.


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

Для управления фокусом при изменении маршрута используется метод focus(). Он позволяет программно установить фокус на элемент и гарантирует доступность:

ally.focus({
  element: document.getElementById('first-input'),
  preventScroll: true
});

Параметры функции focus:

  • element — целевой DOM-элемент.
  • preventScroll — фокус устанавливается без прокрутки страницы.
  • ignoreVisibility — позволяет фокусировать элементы, которые скрыты с помощью CSS.

Использование этих опций важно при роутинге: часто новая страница или компонент появляется динамически, и нужно предотвратить резкое смещение окна или установку фокуса на невидимый элемент.


Контроль фокуса при роутинге

При смене маршрута (например, в SPA на React или Vue) фокус должен логически переходить на новый ключевой элемент. ally.js предоставляет функции для безопасного переноса фокуса:

// После смены маршрута
const mainContent = document.getElementById('main-content');

if (mainContent) {
  ally.focus({
    element: mainContent,
    preventScroll: true
  });
}

Практические рекомендации:

  • На каждой новой странице или компоненте устанавливать фокус на первый интерактивный элемент или на контейнер основного контента.
  • Для модальных окон и всплывающих панелей использовать трап фокуса (trap.tab), чтобы Tab и Shift+Tab циклировали внутри окна.
  • При закрытии модального окна возвращать фокус на элемент, который его открыл.

Управление фокусом внутри модальных окон

ally.js поддерживает трап фокуса для модальных окон, что предотвращает уход фокуса за пределы окна:

import { trap } from 'ally.js';

const modal = document.getElementById('modal');
const trapInstance = trap.focus(modal);

// После закрытия модального окна
trapInstance.disengage();

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

  • Фокус не может покинуть модальное окно, пока активен trap.
  • Можно указать элементы, на которые фокус возвращается после закрытия окна (returnFocus).
  • Работает с динамически добавляемыми элементами внутри окна.

Это критично для SPA, где компоненты создаются и уничтожаются без перезагрузки страницы.


События изменения видимости и доступности

ally.js позволяет следить за изменениями DOM, чтобы автоматически корректировать фокус:

ally.when.visible({
  filter: '#dynamic-content',
  callback: (elements) => {
    ally.focus({ element: elements[0] });
  }
});
  • when.visible — отслеживает элементы, которые стали видимыми.
  • filter — CSS-селектор для конкретного контекста.
  • callback — функция, вызываемая после появления элемента.

Это особенно полезно при роутинге и загрузке асинхронного контента, когда элементы появляются на странице спустя некоторое время.


Совместное использование с роутером

В SPA роутерах, таких как React Router или Vue Router, ally.js интегрируется с жизненным циклом маршрута:

router.afterEach(() => {
  const mainHeading = document.querySelector('h1');
  if (mainHeading) {
    ally.focus({ element: mainHeading });
  }
});

Рекомендации:

  • Ставить фокус на заголовок страницы (h1) или на контейнер основного контента.
  • Обеспечить плавный пользовательский опыт без лишних скачков фокуса.
  • Использовать preventScroll: true, чтобы избежать неожиданных прокруток.

Проверка доступности

ally.js также предоставляет инструменты для проверки доступности фокуса:

const issues = ally.check.focus({ context: document.body });
console.log(issues);
  • check.focus выявляет элементы, на которые невозможно поставить фокус.
  • Полезно при разработке новых маршрутов и компонентов, чтобы избежать “потерянного” фокуса.

Итоговая схема работы

  1. Определение элементов для фокуса (focusable или tabbable).
  2. Программное управление фокусом через focus() и trap.focus().
  3. Автоматическая реакция на динамическое появление элементов (when.visible).
  4. Интеграция с роутером для SPA, установка фокуса на ключевые элементы после смены маршрута.
  5. Проверка доступности и корректировки, чтобы исключить элементы без возможности фокусировки.

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