Обработка Escape и Enter

Библиотека Tabbable предназначена для определения элементов веб-страницы, которые могут получать фокус при навигации с помощью клавиши Tab. Она особенно полезна при создании доступных интерфейсов, модальных окон, диалогов и сложных форм. Помимо определения tabbable-элементов, критически важно корректно обрабатывать события клавиатуры, такие как Escape и Enter, поскольку они напрямую влияют на пользовательский опыт и взаимодействие с интерфейсом.


Основные принципы работы с Escape и Enter

  1. Escape (Esc) Клавиша Escape традиционно используется для закрытия модальных окон, отмены текущих действий или выхода из интерактивных компонентов. В контексте tabbable-элементов это позволяет:

    • Удалять фокус с модального диалога и возвращать его к элементу, с которого был вызван диалог.
    • Прерывать выделение интерактивного элемента, например, отменять автозаполнение в выпадающих списках.
    • Прерывать обработку ввода внутри формы или панели инструментов.
  2. Enter Клавиша Enter применяется для активации элементов управления:

    • Подтверждение действия, например, отправка формы или выбор элемента из списка.
    • Эмуляция клика на кнопку или ссылку.
    • В многоуровневых интерфейсах — переход к следующему логическому шагу.

Определение tabbable-элементов

Для корректной обработки Escape и Enter необходимо сначала определить, какие элементы страницы являются tabbable. В библиотеке Tabbable используется функция:

import { tabbable } from 'tabbable';

const container = document.querySelector('#modal');
const focusableElements = tabbable(container);
  • tabbable(container) возвращает массив элементов, доступных для фокусировки через Tab.
  • Элементы могут включать <button>, <input>, <textarea>, <a href=""> и любые элементы с tabindex="0" или положительным значением tabindex.

Фокусировка может быть ограничена контейнером, что особенно важно при работе с модальными окнами.


Пример обработки Escape

Escape часто используется для выхода из модального окна или диалогового компонента. Реализация может выглядеть следующим образом:

const modal = document.querySelector('#modal');
const openButton = document.querySelector('#openModal');

openButton.addEventListener('click', () => {
  modal.style.display = 'block';
  const elements = tabbable(modal);
  if (elements.length) elements[0].focus();
});

modal.addEventListener('keydown', (event) => {
  if (event.key === 'Escape') {
    modal.style.display = 'none';
    openButton.focus();
  }
});

Особенности реализации:

  • При открытии модального окна фокус переводится на первый tabbable-элемент.
  • При нажатии Escape окно скрывается, а фокус возвращается к кнопке открытия.
  • Можно добавить event.stopPropagation(), если нужно предотвратить обработку Escape другими слушателями.

Пример обработки Enter

Enter должен корректно активировать элемент, на котором находится фокус:

modal.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') {
    const activeElement = document.activeElement;
    if (activeElement.tagName === 'BUTTON') {
      activeElement.click();
    } else if (activeElement.tagName === 'INPUT' && activeElement.type === 'text') {
      console.log('Текст подтверждён:', activeElement.value);
    }
  }
});

Особенности реализации:

  • Проверка document.activeElement позволяет определить текущий tabbable-элемент.
  • Для кнопок вызывается метод click().
  • Для полей ввода можно инициировать специфическую логику (например, отправку данных или переход к следующему элементу).

Рекомендации по сочетанию Escape и Enter

  1. Локализация фокуса Все tabbable-элементы должны оставаться внутри контейнера, чтобы навигация Tab не выходила за пределы модального окна.

  2. Обработчики на контейнере Для предотвращения множественных слушателей клавиш рекомендуется устанавливать обработчики keydown на контейнер, а не на каждый элемент.

  3. Управление состоянием Escape должен возвращать фокус на элемент, инициировавший открытие окна. Enter должен учитывать текущее состояние элемента (например, disabled кнопки игнорируется).

  4. Совместимость с Screen Reader Escape и Enter должны поддерживать стандартные сценарии использования, чтобы элементы оставались доступными для технологий чтения с экрана.


Комбинации с другими клавишами

В некоторых интерфейсах Escape и Enter могут сочетаться с клавишами Shift или Ctrl для расширенной функциональности:

modal.addEventListener('keydown', (event) => {
  if (event.key === 'Enter' && event.shiftKey) {
    console.log('Shift + Enter активирован');
  }
  if (event.key === 'Escape' && event.ctrlKey) {
    console.log('Ctrl + Escape активирован');
  }
});
  • Shift+Enter может использоваться для вставки новой строки в текстовых полях, не закрывая форму.
  • Ctrl+Escape — быстрый выход из сложного интерфейса, например, панели инструментов.

Закрепление tabbable-фокуса после закрытия модального окна

Чтобы избежать потери фокуса при закрытии окна, часто используют паттерн:

let lastFocusedElement;

openButton.addEventListener('click', () => {
  lastFocusedElement = document.activeElement;
  modal.style.display = 'block';
  tabbable(modal)[0].focus();
});

modal.addEventListener('keydown', (event) => {
  if (event.key === 'Escape') {
    modal.style.display = 'none';
    lastFocusedElement.focus();
  }
});

Это гарантирует, что пользователь вернётся к логическому месту взаимодействия, сохраняя контекст и непрерывность навигации.


Интеграция с динамическими элементами

Tabbable работает с динамически добавляемыми элементами. Если элементы появляются после открытия окна, важно обновлять список tabbable-элементов:

function updateFocusable() {
  return tabbable(modal);
}

modal.addEventListener('keydown', (event) => {
  const elements = updateFocusable();
  if (event.key === 'Tab') {
    // логика обхода фокуса
  }
});

Таким образом, Escape и Enter остаются корректно привязанными к актуальным tabbable-элементам даже при изменении DOM.


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