Автоматическое определение фокусируемых элементов

Библиотека Focus-trap предназначена для управления фокусом внутри модальных окон, диалогов и других интерактивных компонентов, ограничивая перемещение по клавиатуре только определённой областью. Это обеспечивает корректное поведение для пользователей с клавиатурой и улучшает доступность (a11y).

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


Алгоритм автоматического определения фокусируемых элементов

Focus-trap использует внутреннюю функцию, которая ищет все элементы внутри контейнера, подходящие для фокуса. Критерии включают:

  1. Селекторы интерактивных элементов:

    • <a> с атрибутом href
    • <button>
    • <input>
    • <select>
    • <textarea>
    • <iframe>
    • [tabindex]
    • [contenteditable]
  2. Состояние доступности элемента:

    • Элемент должен быть видимым (offsetParent !== null)
    • Элемент не должен быть disabled (disabled !== true)
    • Атрибут tabindex ≥ 0 учитывается для управления порядком фокуса
  3. Порядок обхода:

    • Элементы с положительным tabindex обрабатываются первыми в порядке возрастания значения tabindex
    • Элементы без tabindex или с tabindex="0" идут далее в DOM-порядке
    • Элементы с tabindex < 0 игнорируются, поскольку они недоступны для клавиатурного фокуса

Эта система обеспечивает корректный порядок навигации при использовании клавиши Tab и обратной навигации через Shift+Tab.


Настройка автоматического фокуса

Focus-trap предоставляет опции, которые позволяют изменять поведение поиска фокусируемых элементов:

  • initialFocus – элемент, который получает фокус при активации трапа. Может быть CSS-селектором, DOM-узлом или функцией, возвращающей элемент. Если не указан, библиотека автоматически фокусируется на первом доступном элементе.

  • fallbackFocus – элемент, на который фокус перейдет, если первый фокусируемый элемент недоступен. Важен для динамически изменяемого контента.

  • allowOutsideClick – позволяет кликам вне трапа не нарушать управление фокусом, при этом автоматическое определение фокусируемых элементов продолжается в пределах контейнера.

Пример инициализации:

import { createFocusTrap } from 'focus-trap';

const modal = document.getElementById('modal');

const trap = createFocusTrap(modal, {
  initialFocus: '#modal input:first-of-type',
  fallbackFocus: '#modal button.close',
  allowOutsideClick: true
});

trap.activate();

В этом примере первый фокус назначается на первый <input> внутри модального окна, а если его нет — на кнопку закрытия.


Динамическое обновление списка фокусируемых элементов

Focus-trap способен корректно работать с динамическим содержимым, например, при добавлении или удалении элементов в DOM:

  • Каждый раз при перемещении фокуса библиотека пересчитывает список доступных элементов
  • Это позволяет корректно обрабатывать появление новых кнопок, ссылок или полей ввода
  • Опция checkCanFocusTrap может быть использована для асинхронной проверки доступности элемента перед фокусировкой
trap.updateContainerElements(); // пересчитать фокусируемые элементы

Метод особенно полезен для SPA и компонентов с ленивой загрузкой, где элементы появляются после активации модального окна.


Особенности и нюансы

  1. Невидимые элементы: даже если элемент присутствует в DOM, он игнорируется, если display: none, visibility: hidden или opacity: 0 при pointer-events: none.

  2. Элементы с tabindex="-1": могут быть выбраны как initialFocus или fallbackFocus, но не входят в автоматический обход при обычной навигации через Tab.

  3. Смешанные табиндексы: если в контейнере есть элементы с положительными и нулевыми tabindex, порядок обхода формируется следующим образом: сначала элементы с положительными значениями по возрастанию, затем элементы без tabindex или с tabindex="0".

  4. Shadow DOM: фокусируемые элементы внутри Shadow DOM корректно обнаруживаются, если контейнер трапа включает shadow root в качестве корня поиска.


Рекомендации по использованию

  • Всегда указывать fallbackFocus, чтобы избежать ситуации, когда фокус “теряется” при отсутствии доступных элементов.
  • Использовать initialFocus для контроля, какой элемент должен быть выделен при открытии модального окна.
  • При динамическом изменении содержимого вызывать updateContainerElements или повторно активировать трап, чтобы список фокусируемых элементов соответствовал текущему состоянию DOM.
  • Не задавать tabindex вручную без необходимости — автоматическое определение учитывает стандартные интерактивные элементы и обеспечивает правильный порядок обхода.

Эти механизмы делают Focus-trap универсальным инструментом для управления фокусом, минимизируя ручное вмешательство и повышая доступность интерфейсов.