Фильтрация и динамическое обновление

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

Функция фильтрации вызывается через метод tabbable, focusable или query, принимая в качестве аргумента объект с настройками. Основной параметр фильтрации — filter, который может быть:

  • Функцией (element) => boolean — возвращает true для элементов, которые должны остаться, false для исключаемых.
  • CSS-селектором — элементы, соответствующие селектору, будут включены или исключены в зависимости от контекста.

Пример:

const focusableElements = ally.query.focusable({
  filter: (element) => !element.classList.contains('hidden')
});

В этом примере исключаются все элементы с классом hidden.


Использование предопределенных фильтров

Ally.js содержит набор стандартных фильтров для упрощения работы:

  • focusable — возвращает элементы, на которые можно поставить фокус.
  • tabbable — возвращает элементы, доступные через клавишу Tab, игнорируя элементы с tabindex="-1".
  • visible — исключает скрытые элементы (display: none или visibility: hidden).

Фильтры можно комбинировать, используя функцию-обертку:

const tabbableVisible = ally.query.tabbable({
  filter: (element) => ally.query.visible({context: element})
});

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


Динамическое обновление списка элементов

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

let container = document.getElementById('form-container');
let currentFocusable = ally.query.focusable({context: container});

// позже после динамического изменения DOM
currentFocusable = ally.query.focusable({context: container});

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


Реактивная фильтрация через MutationObserver

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

const container = document.getElementById('dynamic-section');

const observer = new MutationObserver(() => {
  const focusableElements = ally.query.focusable({context: container});
  console.log('Обновленный список элементов:', focusableElements);
});

observer.observe(container, {childList: true, subtree: true});

Параметры childList: true и subtree: true обеспечивают отслеживание добавления и удаления элементов на любом уровне внутри контейнера.


Применение фильтрации для управления фокусом

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

const modal = document.getElementById('modal');
const focusableInModal = ally.query.tabbable({
  context: modal,
  filter: (element) => !element.classList.contains('close-button')
});

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


Продвинутая фильтрация с условиями состояния

Фильтры могут учитывать состояние элемента, атрибуты и внешние условия:

  • Игнорирование элементов с disabled:
filter: (el) => !el.disabled
  • Исключение элементов с определенными ARIA-атрибутами:
filter: (el) => el.getAttribute('aria-hidden') !== 'true'
  • Комбинация нескольких условий:
filter: (el) => el.offsetParent !== null && !el.disabled && el.getAttribute('aria-hidden') !== 'true'

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


Оптимизация производительности

При работе с большими деревьями DOM повторная фильтрация всех элементов может быть затратной. Для оптимизации:

  • Ограничивать контекст поиска до контейнера, а не всего документа.
  • Использовать селекторы для первичной фильтрации перед применением функций.
  • Комбинировать tabbable и visible фильтры, чтобы сократить количество элементов, подлежащих проверке.

Пример:

const focusableInputs = ally.query.focusable({
  context: document.getElementById('form'),
  filter: 'input, select, textarea'
});

Этот метод уменьшает нагрузку на браузер и ускоряет динамическое обновление интерактивных элементов.


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