Библиотека ally.js предназначена для упрощения работы с доступностью элементов в веб-приложениях. Один из ключевых инструментов — фильтрация элементов для получения интерактивных узлов, доступных для фокусировки. Фильтры позволяют точно указать, какие элементы должны участвовать в вычислениях, а какие — игнорироваться, что особенно важно при динамически изменяющемся интерфейсе.
Функция фильтрации вызывается через метод tabbable,
focusable или query, принимая в качестве
аргумента объект с настройками. Основной параметр фильтрации —
filter, который может быть:
(element) => boolean —
возвращает true для элементов, которые должны остаться,
false для исключаемых.Пример:
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, который отслеживает
изменения в 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
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 без ручного перебора каждого узла.