Vanilla JavaScript

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

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

Установка и подключение

Установка через npm:

npm install tabbable

Подключение в проект:

import { tabbable } from 'tabbable';
// или через require
const { tabbable } = require('tabbable');

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

Основная функция: tabbable()

Функция tabbable() принимает DOM-элемент в качестве аргумента и возвращает массив элементов, которые можно выделить с помощью клавиши Tab.

const container = document.querySelector('#form-container');
const focusableElements = tabbable(container);
console.log(focusableElements);

Важные моменты:

  • Элементы, скрытые через display: none или visibility: hidden, не включаются.
  • Элементы с атрибутом tabindex="-1" исключаются.
  • Только интерактивные элементы, такие как <button>, <input>, <select>, <textarea>, <a href="#"> считаются табулируемыми.

Расширенные возможности

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

Можно указать контейнер, внутри которого будет происходить поиск, что полезно для модальных окон, всплывающих меню и диалогов:

const modal = document.querySelector('.modal');
const focusables = tabbable(modal);

Фильтрация по состоянию активности

Библиотека автоматически исключает элементы с disabled:

<button disabled>Нельзя табулировать</button>

Такой элемент не попадет в результирующий массив.

Работа с tabindex

tabindex играет ключевую роль:

  • tabindex="0" — элемент доступен по табуляции в обычном порядке.
  • tabindex="-1" — элемент исключен из последовательности табуляции, но может быть фокусирован программно.
  • tabindex="1" и выше — задают приоритет фокусировки. Библиотека сортирует такие элементы по возрастанию tabindex.
const focusables = tabbable(document.body);
focusables.forEach(el => console.log(el.tabIndex));

Интеграция с модальными окнами и диалогами

При работе с модальными окнами важно ограничивать фокус только элементами внутри модального окна. tabbable() позволяет получать массив всех фокусируемых элементов внутри диалога:

const modal = document.querySelector('#myModal');
const focusableInModal = tabbable(modal);
focusableInModal[0].focus();

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

modal.addEventListener('keydown', (e) => {
  if (e.key === 'Tab') {
    const focusables = tabbable(modal);
    const first = focusables[0];
    const last = focusables[focusables.length - 1];

    if (!e.shiftKey && document.activeElement === last) {
      e.preventDefault();
      first.focus();
    } else if (e.shiftKey && document.activeElement === first) {
      e.preventDefault();
      last.focus();
    }
  }
});

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

  • Скрытые элементы: элементы, скрытые через opacity: 0 или находящиеся вне видимой области, все еще могут считаться tabbable. Для полного контроля рекомендуется использовать CSS свойства display и visibility.
  • Фреймворки: библиотека совместима с любыми JavaScript-фреймворками, так как работает напрямую с DOM.
  • Производительность: для больших DOM-деревьев tabbable() работает быстро, но лучше ограничивать область поиска контейнером.

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

  1. Всегда использовать контейнеры для поиска tabbable-элементов, особенно в динамически создаваемых интерфейсах.
  2. Для модальных окон и всплывающих меню реализовать циклическую табуляцию.
  3. Проверять tabindex при динамическом изменении DOM, чтобы не нарушить естественный порядок фокусировки.
  4. Для тестирования доступности интерфейса использовать комбинацию tabbable() и автоматических тестов на клавиатурную навигацию.

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

const modals = document.querySelectorAll('.modal');
modals.forEach(modal => {
  modal.addEventListener('keydown', (e) => {
    if (e.key === 'Tab') {
      const focusables = tabbable(modal);
      const first = focusables[0];
      const last = focusables[focusables.length - 1];

      if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      } else if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      }
    }
  });
});

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

Библиотека Tabbable — ключевой инструмент для соблюдения стандартов доступности (a11y) в современных веб-приложениях, упрощающий управление фокусом и обеспечивающий удобную навигацию для пользователей с клавиатурой.