Управление фокусом

Управление фокусом — критически важная часть работы модальных окон. Featherlight, как минималистичная lightbox-библиотека, не перегружена сложной инфраструктурой доступности, однако предоставляет достаточные механизмы для корректного контроля фокуса при открытии и закрытии модальных окон. Неправильная работа с фокусом приводит к проблемам навигации с клавиатуры, ухудшает доступность и может вызывать логические ошибки в интерфейсе.

В контексте Featherlight под фокусом понимается активный DOM-элемент, получающий ввод с клавиатуры и участвующий в таб-навигации.


Поведение фокуса по умолчанию

При открытии lightbox Featherlight выполняет следующие действия:

  • создаёт контейнер .featherlight
  • помещает внутрь него .featherlight-content
  • добавляет overlay
  • не управляет фокусом автоматически

Это означает, что:

  • активный элемент до открытия модального окна остаётся активным
  • табуляция продолжает работать по элементам основного документа
  • клавиатурная навигация может «уходить» за пределы модального окна

Такое поведение осознанно: Featherlight не навязывает стратегию управления фокусом и оставляет её на уровне реализации.


Фокус при открытии lightbox

Корректная стратегия — переводить фокус внутрь модального окна сразу после его открытия. Featherlight предоставляет для этого события жизненного цикла.

Ключевые события:

  • featherlight-opened
  • afterOpen (опция)

Пример установки фокуса на первый интерактивный элемент:

$.featherlight(content, {
  afterOpen: function() {
    this.$content
      .find('input, button, select, textarea, a[href]')
      .first()
      .focus();
  }
});

Ключевые моменты:

  • используется this.$content, а не поиск по document
  • фокус устанавливается после фактического добавления DOM-узлов
  • при отсутствии интерактивных элементов допустимо фокусировать контейнер

Фокусируемость контейнера

Если контент не содержит интерактивных элементов, контейнер можно сделать фокусируемым вручную:

<div class="modal-content" tabindex="-1">
  ...
</div>

И затем:

afterOpen: function() {
  this.$content.focus();
}

Это позволяет:

  • удерживать фокус внутри модального окна
  • корректно обрабатывать события клавиатуры
  • обеспечить предсказуемое поведение для screen reader-ов

Ограничение таб-навигации (focus trap)

Featherlight не реализует focus trap из коробки. Для полноценного управления фокусом требуется ручная реализация.

Базовый принцип:

  • определить список фокусируемых элементов внутри lightbox
  • перехватывать keydown для клавиши Tab
  • зацикливать фокус внутри модального окна

Пример логики:

afterOpen: function() {
  const $focusable = this.$content.find(
    'a[href], button, input, textarea, select, [tabindex]:not([tabindex="-1"])'
  );
  const first = $focusable.first();
  const last = $focusable.last();

  this.$instance.on('keydown', function(e) {
    if (e.key === 'Tab') {
      if (e.shiftKey && document.activeElement === first[0]) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last[0]) {
        e.preventDefault();
        first.focus();
      }
    }
  });

  first.focus();
}

Здесь используется:

  • $instance — корневой элемент Featherlight
  • локальный обработчик клавиатуры
  • явное зацикливание фокуса

Сохранение и восстановление фокуса

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

Алгоритм:

  1. сохранить document.activeElement перед открытием
  2. восстановить фокус после закрытия

Пример:

let lastFocusedElement;

$(document).on('click', '.open-modal', function() {
  lastFocusedElement = document.activeElement;
  $.featherlight('#modal');
});

$(document).on('featherlight-closed', function() {
  if (lastFocusedElement) {
    lastFocusedElement.focus();
  }
});

Это особенно важно:

  • при использовании клавиатуры
  • при последовательном открытии нескольких модальных окон
  • для доступности (WCAG 2.x)

Фокус и закрытие по клавише Escape

Featherlight по умолчанию закрывается по Esc. При этом:

  • событие featherlight-closed срабатывает всегда
  • фокус не восстанавливается автоматически

Если реализован focus trap, важно:

  • корректно удалять обработчики клавиатуры
  • не оставлять фокус на удалённом DOM-узле

Рекомендуется очищать все связанные слушатели в afterClose:

afterClose: function() {
  this.$instance.off('keydown');
}

Взаимодействие с динамическим контентом

При загрузке контента через AJAX или при динамическом обновлении DOM внутри lightbox:

  • фокусируемые элементы могут появляться позже
  • начальный фокус может указывать на несуществующий элемент

Решение:

  • устанавливать фокус после завершения загрузки
  • повторно вычислять список фокусируемых элементов

Пример для AJAX-контента:

$.featherlight({
  ajax: '/modal.html',
  afterOpen: function() {
    const fl = this;
    setTimeout(function() {
      fl.$content.find('input').first().focus();
    }, 0);
  }
});

Работа с вложенными lightbox-окнами

Featherlight допускает открытие одного lightbox поверх другого. В этом случае управление фокусом усложняется:

  • каждый уровень должен иметь собственный focus trap
  • фокус не должен «протекать» на нижний уровень
  • восстановление фокуса должно учитывать стек окон

Практика:

  • хранить стек фокусных элементов
  • восстанавливать фокус в порядке LIFO
  • привязывать обработчики строго к текущему $instance

Типичные ошибки управления фокусом

Отсутствие установки фокуса

  • пользователь теряет контекст
  • табуляция уходит в фон

Фокус на скрытом элементе

  • некорректная работа клавиатуры
  • ошибки доступности

Глобальные обработчики keydown

  • конфликт между несколькими модальными окнами
  • утечки логики после закрытия

Использование autofocus в HTML

  • непредсказуемое поведение при динамическом DOM
  • конфликт с ручным управлением фокусом

Роль фокуса в доступности Featherlight

Хотя Featherlight не является accessibility-фреймворком, корректное управление фокусом позволяет:

  • приблизить поведение к требованиям WCAG
  • обеспечить работу с клавиатурой без мыши
  • улучшить UX для всех пользователей

Фокус в Featherlight — не автоматическая функция, а архитектурная ответственность разработчика. Именно поэтому понимание жизненного цикла lightbox и точек управления фокусом является обязательным при использовании библиотеки в серьёзных интерфейсах.