Работа с display: none и visibility: hidden

Библиотека Focus-trap предназначена для управления фокусом внутри модальных окон, диалогов, меню и других интерактивных контейнеров, обеспечивая корректное поведение клавиатурной навигации. Особое внимание уделяется элементам, которые скрыты с помощью CSS: display: none и visibility: hidden. Понимание различий между этими свойствами критично для корректной работы Focus-trap.


Отличия display: none и visibility: hidden

  • display: none полностью удаляет элемент из потока документа. Для браузера элемент считается отсутствующим:

    • он не занимает места на странице,
    • события фокуса не могут быть применены,
    • tabindex на таком элементе игнорируется.
  • visibility: hidden скрывает элемент визуально, но оставляет его в DOM и в потоке документа:

    • элемент продолжает занимать место,
    • он доступен для некоторых скриптов и CSS-псевдоклассов,
    • tabindex по умолчанию делает элемент недоступным для клавиши Tab, но при явной установке tabindex="0" элемент может получить фокус.

Поведение Focus-trap с невидимыми элементами

Focus-trap опирается на метод tabbable для определения всех элементов, которые могут быть фокусированы. Основные правила:

  1. Элементы с display: none автоматически исключаются из списка tabbable. Их нельзя выбрать клавишей Tab.
  2. Элементы с visibility: hidden также по умолчанию исключаются из табуляции, если не заданы явные tabindex.
  3. Для корректного функционирования Focus-trap важно, чтобы в момент активации контейнера все интерактивные элементы были видимы и доступны. Иначе библиотека не сможет корректно зациклить фокус внутри контейнера.

Примеры использования

Пример 1: Простое модальное окно

import { createFocusTrap } from 'focus-trap';

const modal = document.getElementById('modal');
const focusTrap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
});

document.getElementById('openModal').addEventListener('click', () => {
  modal.style.display = 'block';
  focusTrap.activate();
});

document.getElementById('closeModal').addEventListener('click', () => {
  modal.style.display = 'none';
  focusTrap.deactivate();
});
  • Здесь display: none полностью убирает модальное окно из DOM-потока, поэтому Focus-trap активируется только после отображения (display: block).

Пример 2: Элемент с visibility: hidden

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

tooltip.style.visibility = 'hidden'; // скрыт визуально
// Элемент с tabindex="0" может быть фокусирован
tooltip.setAttribute('tabindex', '0');

const tooltipTrap = createFocusTrap(tooltip);
tooltipTrap.activate(); 
  • Элемент с visibility: hidden может быть фокусирован при наличии tabindex, но обычно такие элементы лучше скрывать с display: none для предотвращения неожиданных переходов фокуса.

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

  1. Использовать display: none для полного исключения элемента из фокусируемой области. Особенно важно для модальных окон и временных панелей.
  2. Не полагаться на visibility: hidden для исключения элемента из Focus-trap: без явного tabindex элемент всё равно не будет участвовать в цикле, но это может создать проблемы при динамическом изменении стилей.
  3. Актуализировать tabbable элементы перед активацией Focus-trap: если интерактивные элементы добавляются динамически, нужно вызывать focusTrap.updateTabbableNodes().
  4. Не оставлять скрытые элементы с tabindex>0 в DOM без необходимости: это нарушает логический порядок фокуса.

Особенности при динамическом управлении

При динамическом отображении элементов внутри Focus-trap (display: noneblock) нужно учитывать:

  • Активация Focus-trap до отображения приведёт к пустому списку tabbable, что сделает цикл фокуса невозможным.
  • Если элементы скрываются и становятся невидимыми после активации, Focus-trap автоматически исключает их из цикла, поддерживая корректный фокус на оставшихся элементах.
  • Использование updateTabbableNodes() позволяет пересканировать контейнер и добавить новые интерактивные элементы в цикл.
focusTrap.updateTabbableNodes();

Итоговая логика работы с CSS-свойствами

Свойство Влияние на Focus-trap Рекомендация
display: none Полностью исключает из фокуса Использовать для скрытия модальных окон
visibility: hidden Элемент визуально скрыт, но может быть фокусирован при tabindex Использовать с осторожностью, лучше избегать
Динамическое добавление Нужно вызывать updateTabbableNodes() Поддерживать актуальный список tabbable элементов

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