Focus management при навигации

При работе с современными веб-приложениями, особенно одностраничными (SPA), управление фокусом становится критически важным для доступности (accessibility) и удобства взаимодействия с интерфейсом. TanStack Router предоставляет встроенные механизмы для контроля фокуса при переходе между маршрутами, обеспечивая согласованное поведение навигации и соответствие стандартам WCAG.


Автоматическое управление фокусом

По умолчанию TanStack Router поддерживает автоматическое управление фокусом при навигации. Основная идея заключается в том, что после перехода на новый маршрут фокус перемещается на первый интерактивный элемент страницы или на контейнер контента маршрута.

Механизм работает следующим образом:

  1. При переходе на новый маршрут:

    • Router проверяет, есть ли на странице элемент с атрибутом tabindex="-1".
    • Если такой элемент найден, фокус устанавливается на него.
    • Если элемента нет, фокус перемещается на первый интерактивный элемент (button, a[href], input, textarea, select).
  2. Фокус сохраняется между переходами при использовании back и forward в браузере, если состояние маршрута включает информацию о последнем активном элементе.

Пример базовой конфигурации маршрута с фокусом:

import { createRouter, createRoute } from '@tanstack/router';

const rootRoute = createRoute({
  path: '/',
  component: RootComponent,
  onFocus: (el) => {
    // Настраиваем кастомное поведение фокуса при загрузке маршрута
    if (el) el.focus();
  },
});

const router = createRouter({
  routeTree: rootRoute,
});

Здесь ключевой момент — использование колбэка onFocus, который позволяет указать, какой элемент должен получить фокус после навигации.


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

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

Пример вложенного маршрута:

const dashboardRoute = createRoute({
  path: '/dashboard',
  component: Dashboard,
  children: [
    createRoute({
      path: 'settings',
      component: Settings,
      onFocus: (el) => {
        // Фокус на первом input в форме настроек
        const firstInput = el.querySelector('input');
        if (firstInput) firstInput.focus();
      },
    }),
  ],
});

В этом примере при переходе на /dashboard/settings фокус автоматически устанавливается на первый input формы настроек. Такой подход обеспечивает логичную последовательность навигации для пользователей, использующих клавиатуру или вспомогательные технологии.


Настройка задержки фокуса

В некоторых сценариях требуется отложенное выставление фокуса, например, если компоненты загружаются асинхронно. TanStack Router поддерживает использование setTimeout или requestAnimationFrame в onFocus для задержки:

onFocus: (el) => {
  requestAnimationFrame(() => {
    const firstButton = el.querySelector('button');
    if (firstButton) firstButton.focus();
  });
}

Такой подход гарантирует, что фокус будет установлен только после полной отрисовки DOM-элементов маршрута.


Полезные практики для фокус-менеджмента

  • Использовать tabindex="-1" для контейнеров страниц: это позволяет легко программно управлять фокусом, не влияя на естественный порядок табуляции.
  • Выделять основной контент маршрута: после перехода на новый маршрут основной контейнер контента должен получать фокус, что облегчает навигацию для скринридеров.
  • Сохранять фокус при переходах назад: TanStack Router автоматически поддерживает это при правильной конфигурации состояния маршрута.
  • Комбинировать с анимациями и lazy-loading: важно учитывать задержки рендеринга при использовании динамических компонентов, чтобы фокус не потерялся.

Доступность и стандарты WCAG

Фокус-менеджмент напрямую связан с доступностью:

  • Визуальные индикаторы фокуса помогают пользователям клавиатуры понимать текущую позицию.
  • Перемещение фокуса на главный контейнер маршрута позволяет скринридерам озвучивать новый контент сразу после навигации.
  • Контроль за последовательностью табуляции предотвращает “фокусные ловушки”, когда пользователь застревает внутри компонента.

TanStack Router обеспечивает интеграцию с этим подходом на уровне маршрутов, что уменьшает необходимость вручную писать обработчики на каждый переход.


Кастомизация поведения

TanStack Router предоставляет гибкие API для настройки поведения фокуса:

  • onFocus(el) — кастомная логика при переходе на маршрут.
  • defaultFocusElement — элемент, на который устанавливается фокус, если колбэк не задан.
  • preserveFocus — опция для сохранения текущего фокуса при определённых переходах.

Пример использования:

const profileRoute = createRoute({
  path: '/profile',
  component: Profile,
  onFocus: (el) => el.querySelector('h1')?.focus(),
  preserveFocus: true,
});

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


Хотите, я могу сделать следующую часть о интеграции focus management с React Transition Group и анимациями переходов в TanStack Router для сложных интерфейсов? Это логичное продолжение темы.