Фокус и навигация с клавиатуры

Unpoly предоставляет гибкий механизм управления фокусом и поддерживает удобную навигацию с клавиатуры, что особенно важно для создания динамичных интерфейсов без полной перезагрузки страницы. Работа с фокусом и клавишами тесно связана с концепцией контекста обновления и слотов целевых элементов в Unpoly.


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

Когда Unpoly обновляет часть страницы через up.replace, up.append или другие методы, важно корректно переназначать фокус. По умолчанию Unpoly пытается автоматически установить фокус на первый интерактивный элемент нового фрагмента:

  • <input>, <textarea>, <select>
  • <button> и <a href>
  • Элементы с атрибутом [autofocus]

Если в DOM присутствует элемент с autofocus, Unpoly отдаёт ему приоритет. В противном случае фокус ставится на первый интерактивный элемент внутри обновленного контейнера.

Для явного управления фокусом используется событие up:fragment:loaded:

up.on('up:fragment:loaded', function(event) {
  // event.fragment — обновленный фрагмент
  const input = event.fragment.querySelector('input[name="search"]');
  if (input) {
    input.focus();
  }
});

Здесь event.fragment — это корневой элемент нового фрагмента, что позволяет безопасно выбирать элементы внутри него.


Клавиатурная навигация и «hotkeys»

Unpoly поддерживает настройку пользовательских клавиш для управления действиями страницы без полной перезагрузки. Для этого используется метод up.key:

up.key('ctrl+s', function(event) {
  event.preventDefault();
  up.submit('#form-search');
});

Особенности:

  • Первый аргумент принимает строку с комбинацией клавиш ('ctrl+s', 'alt+arrowup', 'shift+enter').
  • Второй аргумент — обработчик события, которому передается объект KeyboardEvent.
  • Возврат false или вызов event.preventDefault() предотвращает стандартное поведение браузера.

Кроме одиночных элементов, Unpoly может реагировать на клавиши в контексте фрагмента:

up.fragment('div.list').on('up:key:enter', function(event) {
  const selected = event.fragment.querySelector('.selected');
  if (selected) {
    up.follow(selected);
  }
});

Здесь обработчик срабатывает только если фокус находится внутри указанного фрагмента.


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

При навигации по ссылкам через Unpoly (up.follow) поведение фокуса зависит от двух факторов:

  1. Перезаписывается ли контейнер целиком — если да, Unpoly пытается восстановить фокус на элементе, который был активен до обновления.
  2. Наличие атрибута up-autofocus — можно явно указать элемент, который должен получить фокус после загрузки:
<input type="text" up-autofocus>

Атрибут up-autofocus работает аналогично стандартному HTML-атрибуту, но совместим с динамическим обновлением через Unpoly.

Для восстановления фокуса после обновления страницы можно использовать up.fragment.restoreFocus():

up.on('up:fragment:loaded', function(event) {
  up.fragment.restoreFocus(event.fragment);
});

Эта функция ориентируется на последний активный элемент до обновления и безопасно возвращает на него фокус, если он всё ещё существует в DOM.


Навигация по спискам с клавиатуры

Для интерактивных списков или таблиц часто необходимо реализовать перемещение по элементам с клавишами ArrowUp и ArrowDown. Пример с использованием фокусировки на элементах списка:

const list = document.querySelector('.items-list');

up.key('arrowdown', function(event) {
  const current = list.querySelector(':focus');
  if (current && current.nextElementSibling) {
    current.nextElementSibling.focus();
    event.preventDefault();
  }
});

up.key('arrowup', function(event) {
  const current = list.querySelector(':focus');
  if (current && current.previousElementSibling) {
    current.previousElementSibling.focus();
    event.preventDefault();
  }
});

Такое решение работает совместно с динамическими обновлениями через Unpoly, если список находится внутри фрагмента и поддерживает делегирование событий на новый контент.


События фокуса и видимости

Unpoly предоставляет события, связанные с изменением фокуса и видимости элементов:

  • up:fragment:shown — фрагмент появился на странице. Можно назначить фокус на первый интерактивный элемент.
  • up:fragment:hidden — фрагмент удалён или скрыт. Позволяет очистить фокус или отменить активные клавиатурные слушатели.
  • up:focus:received и up:focus:lost — срабатывают при входе или выходе фокуса на элемент внутри фрагмента.

Пример с управлением фокусом при показе модального окна:

up.on('up:fragment:shown', '.modal', function(event) {
  const firstInput = event.fragment.querySelector('input');
  if (firstInput) firstInput.focus();
});

Резюме ключевых особенностей

  • Автоматическое управление фокусом: Unpoly фокусируется на интерактивных элементах после обновления фрагментов.
  • Явное управление через события: up:fragment:loaded, up:fragment:shown, up:focus:received.
  • Гибкая клавиатурная навигация: метод up.key для глобальных и локальных комбинаций.
  • Восстановление фокуса: up.fragment.restoreFocus() обеспечивает удобный UX при динамических переходах.
  • Контекстное реагирование на клавиши: события привязываются к конкретным фрагментам, что предотвращает конфликт клавиш при множественных интерактивных областях.

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