0Pricing
React Academy · Урок

Управление фокусом и клавиатурные ловушки в модальных окнах

Удерживайте фокус внутри открытых модальных окон для доступности с помощью useRef и обработчиков событий

«Управление фокусом и клавиатурные ловушки в модальных окнах» — бесплатный урок React Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения React Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс React Academy содержит 4 уроков всего.

Добро пожаловать

На этом уроке Вы реализуете ловушку фокуса внутри модального окна, чтобы пользователи клавиатуры не могли выйти из открытого окна с помощью Tab — это ключевое требование доступности (WCAG 2.4.3).

Зачем нужны ловушки фокуса

Когда модальное окно открыто, пользователи клавиатуры должны взаимодействовать только с его содержимым. Без ловушки фокуса нажатие Tab перемещает фокус к элементам за слоем модального окна — визуально они скрыты, но всё ещё доступны.

Что такое ловушка фокуса

Ловушка фокуса перехватывает события клавиш Tab и Shift+Tab и, когда фокус должен покинуть модальное окно, возвращает его к первому или последнему элементу, доступному для фокуса, внутри окна.

Поиск элементов, доступных для фокуса

Используйте querySelectorAll с селектором, охватывающим все интерактивные элементы. Исключите отключённые и скрытые элементы.
const FOCUSABLE = [
  'a[href]', 'button:not([disabled])',
  'input:not([disabled])', 'select', 'textarea',
  '[tabindex]:not([tabindex="-1"])'
].join(',');

const focusable = Array.from(
  modalRef.current.querySelectorAll(FOCUSABLE)
);

Реализация ловушки

Прослушивайте событие keydown на контейнере модального окна. При нажатии Tab, если фокус находится на последнем элементе, переместите его на первый. При нажатии Shift+Tab, если фокус находится на первом элементе, переместите его на последний.
function handleTab(e) {
  const focusable = /* array of focusable elements */;
  const first = focusable[0];
  const last = focusable[focusable.length - 1];
  if (e.key !== 'Tab') return;
  if (e.shiftKey) {
    if (document.activeElement === first) {
      e.preventDefault(); last.focus();
    }
  } else {
    if (document.activeElement === last) {
      e.preventDefault(); first.focus();
    }
  }
}

Перемещение фокуса при открытии

Когда модальное окно открывается, переместите фокус на первый доступный для фокуса элемент внутри него (или на сам контейнер модального окна с помощью `tabIndex=-1`). Это не позволяет фокусу остаться на кнопке, открывшей окно.
useEffect(() => {
  if (isOpen) {
    const first = modalRef.current?.querySelector(FOCUSABLE);
    first?.focus();
  }
}, [isOpen]);

Восстановление фокуса при закрытии

Когда модальное окно закрывается, верните фокус элементу, который был активен до его открытия. Сохраните ссылку на `document.activeElement` в момент открытия окна.
const previousFocus = useRef(null);

useEffect(() => {
  if (isOpen) {
    previousFocus.current = document.activeElement;
  } else {
    previousFocus.current?.focus();
  }
}, [isOpen]);

Использование библиотеки focus-trap-react

Для production-использования рассмотрите библиотеку **focus-trap-react**. Она обрабатывает все особые случаи: shadow DOM, iframe, динамически добавленные элементы, доступные для фокуса, и деактивацию по клавише Escape.
import FocusTrap from 'focus-trap-react';

{isOpen && (
  <FocusTrap>
    <div role="dialog" aria-modal="true">
      {children}
    </div>
  </FocusTrap>
)}

Атрибуты ARIA для модальных окон

Добавьте `role="dialog"` и `aria-modal="true"` в контейнер модального окна. Добавьте `aria-labelledby`, указывающий на заголовок модального окна. Это сообщает программам чтения с экрана, что диалог является модальным, и позволяет правильно его объявить.
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
>
  <h2 id="modal-title">Confirm Delete</h2>
  ...
</div>

Атрибут inert для фона

Атрибут HTML `inert` у контейнера удаляет все его элементы из порядка фокуса и области доступности программ чтения с экрана. Установите `document.getElementById('root').inert = true`, пока модальное окно открыто, чтобы надёжно заблокировать фон.
useEffect(() => {
  const root = document.getElementById('root');
  if (isOpen) root.setAttribute('inert', '');
  else root.removeAttribute('inert');
}, [isOpen]);

Быстрая проверка

Что должно произойти, когда пользователь клавиатуры нажимает Tab на последнем доступном для фокуса элементе внутри модального окна с ловушкой фокуса?

Итоги

Вы реализовали ловушку фокуса: нашли доступные для фокуса элементы, перехватили Tab/Shift+Tab, переместили фокус при открытии и восстановили его при закрытии. Для production-использования применяйте focus-trap-react и добавляйте role='dialog' и aria-modal для программ чтения с экрана.

Далее

Следующий урок: **Всплывающие подсказки и раскрывающиеся меню с порталами** — Вы отрисуете подсказки и раскрывающиеся списки, обходя контейнеры с overflow-hidden, с помощью createPortal.

Часто задаваемые вопросы

Урок «Управление фокусом и клавиатурные ловушки в модальных окнах» бесплатный?

Да — полный текст урока «Управление фокусом и клавиатурные ловушки в модальных окнах» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс React Academy, подпишись на CoddyKit PRO. Курс React Academy содержит 4 уроков всего.

Чему я научусь в уроке «Управление фокусом и клавиатурные ловушки в модальных окнах»?

Удерживайте фокус внутри открытых модальных окон для доступности с помощью useRef и обработчиков событий Ты практикуешь React Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать React Academy?

Предыдущий опыт не требуется. React Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Управление фокусом и клавиатурные ловушки в модальных окнах»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке React Academy?

Да. Каждый урок React Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Что такое React Portals
  2. Создание модального компонента с порталами
  3. Управление фокусом и клавиатурные ловушки в модальных окнах
  4. Всплывающие подсказки и раскрывающиеся меню с порталами
← Назад к React Academy