Настройка собственных фильтров

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

По умолчанию Tabbable учитывает следующие критерии:

  • Элемент не скрыт (display: none или visibility: hidden)
  • Элемент не отключён (disabled атрибут)
  • Элемент имеет валидный tabindex или является нативно фокусируемым (<a href>, <button>, <input> и т.п.)

Однако для сложных интерфейсов этого набора фильтров может быть недостаточно. В таких случаях необходимо настраивать собственные фильтры.


Настройка собственных фильтров

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

import { tabbable } from 'tabbable';

const customFilter = (element) => {
  // Игнорировать все кнопки с классом .ignore-tab
  if (element.matches('.ignore-tab')) return false;
  
  // Разрешить табуляцию для элементов с data-tab-allow
  if (element.hasAttribute('data-tab-allow')) return true;

  // Применить стандартные правила Tabbable
  return true;
};

const elements = tabbable(document.body, { filter: customFilter });
console.log(elements);

Ключевые моменты:

  1. Функция фильтрации получает DOM-элемент и должна возвращать true для элементов, которые допустимо включать, и false для исключаемых.
  2. Можно комбинировать стандартные правила Tabbable с собственными условиями.
  3. Фильтры могут учитывать любые свойства элемента: атрибуты, классы, родительские контейнеры или пользовательские данные (data-*).

Примеры сложных фильтров

Исключение элементов внутри модальных окон

Для интерфейсов с модальными окнами иногда необходимо игнорировать все элементы вне активного окна:

const modal = document.querySelector('.modal.active');

const modalFilter = (element) => modal.contains(element);

const tabbableElements = tabbable(document.body, { filter: modalFilter });

Ограничение табуляции по роли элемента

Иногда нужно включать в табуляцию только элементы с определёнными ARIA-ролями:

const roleFilter = (element) => ['button', 'link', 'textbox'].includes(element.getAttribute('role'));

const tabbableElements = tabbable(document.body, { filter: roleFilter });

Динамическая фильтрация по состоянию интерфейса

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

const activeOnlyFilter = (element) => element.classList.contains('is-active');

const tabbableElements = tabbable(document.body, { filter: activeOnlyFilter });

Комбинация нескольких фильтров

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

const combineFilters = (...filters) => (element) =>
  filters.every((filter) => filter(element));

const combinedFilter = combineFilters(
  (el) => !el.disabled,
  (el) => el.offsetParent !== null,
  (el) => el.getAttribute('data-tab-allow') !== 'false'
);

const elements = tabbable(document.body, { filter: combinedFilter });

В этом примере элемент должен пройти все проверки: не быть отключённым, быть видимым и иметь разрешение на табуляцию через data-tab-allow.


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

  • Для крупных приложений лучше централизовать фильтры, чтобы не дублировать логику в разных местах.
  • Использование атрибутов data-* позволяет легко управлять включением и исключением элементов без изменения классов и структуры DOM.
  • При работе с динамическими компонентами фильтры должны учитывать возможность изменения состояния элемента после рендеринга.
  • Всегда тестировать фильтры с клавиатурной навигацией, чтобы избежать ситуаций, когда пользователь «застрял» вне интерактивных элементов.

Настройка собственных фильтров в Tabbable делает управление фокусом гибким и безопасным. Грамотно спроектированные фильтры позволяют создавать доступные и предсказуемые интерфейсы даже в сложных SPA или компонентных библиотеках.