Динамически создаваемые элементы

Библиотека Tabbable предназначена для определения элементов страницы, на которые можно перейти с помощью клавиши Tab. Основная цель — управление фокусом и доступностью динамически создаваемого контента. Элементы считаются «фокусируемыми», если они могут принимать фокус по стандартам HTML и не имеют атрибута tabindex="-1" или disabled.

Ключевой особенностью Tabbable является динамическое определение фокусируемых элементов, что особенно важно для SPA (Single Page Application) или страниц с динамическим рендерингом.


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

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

npm install tabbable

Импорт в проект:

import { tabbable } from 'tabbable';

Функция tabbable(root, options) принимает два аргумента: корневой элемент DOM, в пределах которого будет выполняться поиск, и необязательный объект настроек.


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

Простейший пример:

const container = document.getElementById('dynamic-container');
const focusableElements = tabbable(container);
console.log(focusableElements);

Особенности работы:

  • Возвращается массив DOM-элементов в порядке их табуляции.
  • Игнорируются элементы с display: none или visibility: hidden.
  • Элементы с tabindex="-1" исключаются.
  • Элементы с disabled также не включаются.

Опционально можно использовать параметр includeContainer:

const elementsIncludingContainer = tabbable(container, { includeContainer: true });

Если контейнер сам является фокусируемым, он будет добавлен в результирующий массив.


Динамически создаваемые элементы

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

function updateTabbableElements() {
    const container = document.getElementById('dynamic-container');
    const focusable = tabbable(container);
    console.log(focusable);
}

// Пример добавления нового элемента
const newButton = document.createElement('button');
newButton.textContent = 'Новый элемент';
document.getElementById('dynamic-container').appendChild(newButton);

// Обновление списка фокусируемых элементов
updateTabbableElements();

Важно: Tabbable не отслеживает DOM автоматически. Любое добавление или удаление элементов требует повторного вызова функции.


Фильтрация и исключения

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

const focusable = tabbable(container, {
    includeContainer: false,
    displayCheck: 'full' // проверка видимости: 'full' или 'non-zero-area'
});
  • displayCheck: 'full' — учитываются только полностью видимые элементы.
  • displayCheck: 'non-zero-area' — учитываются элементы с ненулевой областью (иногда полезно для анимаций).

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


Работа с tabindex

tabindex играет ключевую роль в управлении порядком табуляции:

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

При динамическом добавлении элементов стоит явно задавать tabindex, если требуется определённый порядок.

const dynamicInput = document.createElement('input');
dynamicInput.setAttribute('tabindex', '0');
container.appendChild(dynamicInput);

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

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

const focusable = tabbable(container);
if (focusable.length > 0) {
    focusable[0].focus(); // первый элемент получает фокус
}

Для циклической навигации по динамическим элементам:

let currentIndex = 0;

function focusNext() {
    const elements = tabbable(container);
    currentIndex = (currentIndex + 1) % elements.length;
    elements[currentIndex].focus();
}

document.addEventListener('keydown', (e) => {
    if (e.key === 'Tab') {
        e.preventDefault();
        focusNext();
    }
});

Совместимость с библиотеками UI

Tabbable хорошо интегрируется с:

  • Модальными окнами
  • Аккордеонами
  • Динамическими списками

Особенно важно вызывать tabbable() после рендеринга динамических компонентов, чтобы получить актуальный порядок табуляции. В React или Vue это обычно делается в useEffect или mounted хукe после изменения DOM.


Производительность

При больших списках элементов важно учитывать:

  • Tabbable выполняет рекурсивный обход DOM.
  • Использование опции displayCheck: 'non-zero-area' может ускорить обработку для сложных UI.
  • Для часто обновляемого динамического контента рекомендуется кешировать результаты и обновлять их только при изменении структуры.

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

Примеры реальных сценариев:

  1. Динамическая форма: новые поля появляются по кнопке «Добавить». С помощью Tabbable можно гарантировать правильный порядок фокуса.
  2. Модальные окна с динамическим контентом: автоматически фокусируются на первом интерактивном элементе.
  3. Табы и аккордеоны: фокус корректно переходит между видимыми элементами.

Использование Tabbable обеспечивает доступность (a11y) и предотвращает потерю фокуса при изменении DOM.