Проблемы в iOS Safari

Библиотека Focus-trap предназначена для управления фокусом внутри модальных окон, всплывающих панелей и других интерактивных областей. Основная цель — ограничить навигацию клавишей Tab так, чтобы пользователь оставался внутри выбранного контейнера. Несмотря на универсальность решения, при работе в iOS Safari возникают специфические проблемы, связанные с особенностями мобильной платформы и поведения фокуса в браузере.


Особенности управления фокусом на iOS

  1. Отсутствие стандартного Tab-фокуса На мобильных устройствах клавиатура не содержит клавиш Tab, поэтому привычный цикл фокусировки через Tab не работает. Focus-trap по умолчанию полагается на событие keydown для клавиши Tab, что делает часть его логики неэффективной на iOS. Это особенно заметно при использовании внешних клавиатур, где поведение может отличаться от настольного Safari.

  2. Всплывающая клавиатура и смещение элементов При появлении виртуальной клавиатуры Safari изменяет размеры окна (window.innerHeight) и может автоматически прокручивать страницу так, чтобы активное поле ввода было видимо. Это приводит к неожиданным сдвигам контента внутри focus-trap контейнера, нарушая визуальный порядок элементов и создавая ощущение “прыгающего” интерфейса.

  3. Фокус на скрытых элементах iOS Safari иногда позволяет установить фокус на элементы, которые визуально скрыты или перекрыты другими слоями. Библиотека Focus-trap использует tabindex для управления доступностью элементов, однако Safari может игнорировать этот атрибут в некоторых условиях, например, если элемент имеет display: none или opacity: 0. Это создаёт риск выхода фокуса за пределы заданного контейнера.


Поведенческие баги при активации Focus-trap

  • Непредсказуемый первый фокус При инициализации focus-trap на модальном окне первый элемент может не получать фокус автоматически. Это связано с тем, что iOS Safari требует явного пользовательского взаимодействия перед установкой фокуса (focus()), иначе вызов игнорируется.

  • Проблемы с touch-событиями Клик или тап по элементу внутри focus-trap может не приводить к ожидаемому срабатыванию focus. В некоторых случаях приходится программно вызывать focus() в обработчике touchstart или click, чтобы корректно обновить состояние фокуса.

  • Сбой restoreFocus Focus-trap предоставляет опцию returnFocusOnDeactivate, которая возвращает фокус на элемент, который был активен до активации ловушки. На iOS Safari эта функция работает нестабильно: если элемент исходного фокуса находится за пределами видимой области или скрыт при деактивации trap, фокус может потеряться или установиться на body.


Методы обхода ограничений

  1. Использование initialFocus Задание явного элемента через параметр initialFocus позволяет избежать проблем с первым фокусом. На iOS Safari лучше выбирать интерактивный элемент, который уже присутствует в DOM и видим на экране.

    focusTrap.createFocusTrap(modalElement, {
        initialFocus: modalElement.querySelector('input[type="text"]'),
        escapeDeactivates: true,
        clickOutsideDeactivates: true
    });
  2. Обработка событий touch Для обеспечения корректной работы фокуса при касаниях можно добавить обработку touchstart с вызовом focus():

    modalElement.addEventListener('touchstart', (event) => {
        if (!event.target.matches('input, button, textarea')) return;
        event.target.focus();
    });
  3. Динамическая корректировка размеров При открытии модального окна необходимо учитывать изменение высоты окна и прокрутку контента. Использование window.visualViewport.height и явная установка scrollTop для контейнера focus-trap помогает избежать “прыжков”:

    modalElement.style.maxHeight = `${window.visualViewport.height}px`;
    modalElement.scrollTop = 0;
  4. Проверка видимости элементов Перед активацией focus-trap рекомендуется фильтровать элементы, которые фактически могут получать фокус:

    const focusableElements = Array.from(modalElement.querySelectorAll('a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])'))
        .filter(el => el.offsetParent !== null && !el.disabled);

Ограничения и рекомендации

  • На мобильных устройствах, особенно iOS Safari, полное воспроизведение поведения desktop Tab navigation невозможно. Focus-trap работает частично, и важно проектировать интерфейс с учётом этого.
  • Не рекомендуется полагаться исключительно на программный вызов focus(). Необходимо сочетать его с явными интерактивными действиями пользователя.
  • Оптимизация визуальных переходов и scroll management критична для предотвращения сдвигов и потери контекста пользователя.

Итоговое наблюдение

iOS Safari накладывает ряд специфических ограничений на управление фокусом, что требует комбинированного подхода: настройка параметров focus-trap (initialFocus, returnFocusOnDeactivate), программная обработка touch и корректировка размеров контейнеров. Учитывая эти особенности, библиотека остаётся полезным инструментом для обеспечения доступности, но требует дополнительной адаптации под мобильные платформы.