Кастомные стратегии табуляции

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

Определение фокусируемых элементов

Базовый метод библиотеки позволяет получить массив элементов, доступных для табуляции:

import tabbable from 'tabbable';

const focusableElements = tabbable(document.body);
console.log(focusableElements);

Метод учитывает следующие критерии:

  • Элемент видим (не display: none и не visibility: hidden).
  • Элемент не имеет атрибута disabled.
  • Элемент поддерживает стандартную табуляцию (<input>, <button>, <a href>, <textarea>, <select> и элементы с tabindex >= 0).

Кастомизация стратегий табуляции

Tabbable предоставляет возможность расширенной настройки, позволяя определить собственные правила выбора фокусируемых элементов. Это особенно важно для сложных интерфейсов, где стандартные критерии не подходят.

Использование кастомного фильтра

Можно передать функцию, которая будет определять, следует ли включать элемент в список:

const focusable = tabbable(document.body, {
  filter: element => element.tagName !== 'INPUT' || element.type !== 'hidden'
});

В этом примере исключаются скрытые поля ввода, даже если у них tabindex установлен.

Фокусируемые группы

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

const sections = document.querySelectorAll('.section');
sections.forEach(section => {
  const focusables = tabbable(section, {
    filter: element => !element.classList.contains('skip')
  });
  console.log(focusables);
});

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

Расширение поведения через tabindex

Tabindex является ключевым инструментом кастомизации. Он позволяет упорядочивать фокусируемые элементы произвольно, независимо от их положения в DOM:

  • tabindex="0" — стандартный порядок табуляции.
  • tabindex="-1" — элемент исключается из последовательности табуляции, но доступен программно.
  • tabindex="n" (n > 0) — задает конкретную позицию в последовательности.

Tabbable учитывает tabindex при построении списка и позволяет реализовать сложные схемы навигации, например при управлении с клавиатуры большим набором интерактивных элементов.

Кастомные стратегии сортировки

По умолчанию Tabbable сортирует элементы в порядке их появления в DOM. Для создания нестандартного порядка можно использовать кастомные функции сортировки:

const focusables = tabbable(document.body).sort((a, b) => {
  return a.dataset.priority - b.dataset.priority;
});

Эта стратегия позволяет сортировать элементы на основе пользовательских атрибутов (data-priority) или других критериев.

Работа с вложенными компонентами

В современных SPA часто встречаются вложенные компоненты, например модальные окна, аккордеоны, панели с вкладками. Для корректной табуляции важно:

  • Игнорировать скрытые контейнеры.
  • Обрабатывать только активные вкладки.
  • Обеспечивать циклическую навигацию внутри модального окна.

Пример циклической навигации:

const modal = document.querySelector('.modal');
const focusables = tabbable(modal);
modal.addEventListener('keydown', e => {
  if (e.key === 'Tab') {
    const first = focusables[0];
    const last = focusables[focusables.length - 1];
    if (e.shiftKey && document.activeElement === first) {
      last.focus();
      e.preventDefault();
    } else if (!e.shiftKey && document.activeElement === last) {
      first.focus();
      e.preventDefault();
    }
  }
});

Настройка игнорируемых элементов

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

const focusables = tabbable(document.body, {
  filter: el => !el.classList.contains('no-tab')
});

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

  • Всегда использовать tabindex для контроля порядка фокусировки в сложных интерфейсах.
  • Разделять фокусируемые элементы на логические группы для модальных окон и виджетов.
  • Использовать кастомные фильтры, чтобы исключать элементы, которые не должны быть интерактивными.
  • При работе с динамическим DOM пересчитывать список фокусируемых элементов после изменения структуры страницы.
  • Для доступности интерфейса важно проверять корректность работы Tab и Shift+Tab во всех ключевых контейнерах.

Tabbable обеспечивает гибкую и надёжную основу для управления клавиатурной навигацией, позволяя реализовывать как стандартные, так и сложные пользовательские схемы табуляции.