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

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

Ключевые моменты работы библиотеки:

  • Доступность по стандарту WAI-ARIA. Tabbable учитывает все HTML-атрибуты, влияющие на фокус: tabindex, disabled, aria-hidden, hidden.
  • Фильтрация элементов. Только видимые и интерактивные элементы включаются в список табуляции.
  • Совместимость с любыми элементами. Поддерживаются кнопки, ссылки, поля формы, элементы с tabindex ≥ 0.

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

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

npm install tabbable

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

import { tabbable, isTabbable } from 'tabbable';

tabbable(element) возвращает массив всех фокусируемых элементов внутри контейнера element. Функция isTabbable(element) проверяет, можно ли установить фокус на конкретный элемент.

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

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

const container = document.querySelector('.slide');
const focusableElements = tabbable(container);

Особенности фильтрации:

  • Элементы с display: none или visibility: hidden исключаются.
  • Элементы с disabled или aria-hidden="true" игнорируются.
  • Элементы с tabindex="-1" не включаются в список, но остаются доступными для программного фокуса.

Навигация между слайдами

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

function focusFirstElement(slide) {
  const elements = tabbable(slide);
  if (elements.length > 0) {
    elements[0].focus();
  }
}

function focusLastElement(slide) {
  const elements = tabbable(slide);
  if (elements.length > 0) {
    elements[elements.length - 1].focus();
  }
}

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

Замкнутый фокус (Focus Trap)

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

function trapFocus(slide) {
  const elements = tabbable(slide);
  const first = elements[0];
  const last = elements[elements.length - 1];

  slide.addEventListener('keydown', (event) => {
    if (event.key === 'Tab') {
      if (event.shiftKey) {
        if (document.activeElement === first) {
          last.focus();
          event.preventDefault();
        }
      } else {
        if (document.activeElement === last) {
          first.focus();
          event.preventDefault();
        }
      }
    }
  });
}

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

Динамические изменения контента

Когда слайды динамически изменяются (например, добавляются новые элементы), важно обновлять список фокусируемых элементов каждый раз при изменении DOM:

function updateFocusableElements(slide) {
  return tabbable(slide);
}

Это позволяет корректно обрабатывать появление новых кнопок, ссылок или полей ввода без нарушения последовательности табуляции.

Советы по производительности

  • Минимизировать вызовы tabbable() для больших контейнеров. Лучше кешировать результат и обновлять только при изменении DOM.
  • Использовать isTabbable() для проверки одиночных элементов без перебора всех дочерних узлов.
  • Для сложных интерактивных слайдеров создавать отдельный менеджер фокуса, который отслеживает активный слайд и его элементы.

Интеграция с фреймворками

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

import { useEffect, useRef } from 'react';
import { tabbable } from 'tabbable';

function Slide({ children }) {
  const slideRef = useRef(null);

  useEffect(() => {
    const elements = tabbable(slideRef.current);
    if (elements.length > 0) {
      elements[0].focus();
    }
  }, [children]);

  return <div ref={slideRef}>{children}</div>;
}

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

Особенности обработки элементов с tabindex

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

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

  • Слайдеры с формами и интерактивными карточками.
  • Модальные окна и диалоги.
  • Меню и панели инструментов, где фокус не должен покидать область до закрытия.

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