Keyboard navigation

Keyboard navigation является важной частью интерактивных веб-приложений, обеспечивая доступность и удобство управления без мыши. В фреймворке Fresh взаимодействие с клавиатурой строится на сочетании возможностей стандартного DOM, событий клавиатуры и реактивной модели компонентов.


Обработка событий клавиатуры

В Fresh события клавиатуры обрабатываются через стандартные обработчики onkeydown, onkeyup и onkeypress. Эти обработчики могут быть привязаны к элементам с помощью JSX:

export default function KeyboardExample() {
  const handleKeyDown = (e: KeyboardEvent) => {
    if (e.key === "Enter") {
      console.log("Enter pressed");
    }
  };

  return (
    <input
      type="text"
      placeholder="Нажмите Enter"
      onKeyD own={handleKeyDown}
    />
  );
}

Особенности работы с событиями:

  • onKeyDown вызывается при нажатии клавиши, до появления её символа в поле ввода.
  • onKeyUp срабатывает после отпускания клавиши.
  • onKeyPress устарел и не рекомендуется для использования в современных приложениях.

В Fresh, как и в других реактивных фреймворках, важно учитывать, что события передаются через синтетический слой, поэтому доступ к стандартным свойствам KeyboardEvent остаётся.


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

Фокусировка элементов критически важна для навигации с клавиатуры. Fresh поддерживает работу с ref через стандартные DOM-референции:

import { useRef } from "preact/hooks";

export default function FocusExample() {
  const inputRef = useRef<HTMLInputElement>(null);

  const focusInput = () => {
    inputRef.current?.focus();
  };

  return (
    <>
      <button onCl ick={focusInput}>Фокус на input</button>
      <input ref={inputRef} type="text" />
    </>
  );
}

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

  • Метод focus() активирует элемент для ввода.
  • Атрибут tabIndex управляет порядком перехода через клавишу Tab.
  • Для интерактивных компонентов (button, input, select) значение tabIndex по умолчанию установлено автоматически.

Навигация по спискам и меню

Для создания доступных компонентов с клавиатурой часто используют обработку клавиш ArrowUp, ArrowDown, Home, End. Пример управления фокусом в меню:

import { useState, useRef } from "preact/hooks";

export default function MenuExample() {
  const [activeIndex, setActiveIndex] = useState(0);
  const items = ["Элемент 1", "Элемент 2", "Элемент 3"];
  const refs = items.map(() => useRef<HTMLLIElement>(null));

  const handleKeyDown = (e: KeyboardEvent) => {
    if (e.key === "ArrowDown") {
      setActiveIndex((prev) => (prev + 1) % items.length);
    } else if (e.key === "ArrowUp") {
      setActiveIndex((prev) => (prev - 1 + items.length) % items.length);
    }
  };

  return (
    <ul onKeyD own={handleKeyDown} tabIndex={0}>
      {items.map((item, i) => (
        <li
          ref={refs[i]}
          key={i}
          style={{ background: i === activeIndex ? "lightblue" : "transparent" }}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}

Принципы:

  • Использование массива refs позволяет динамически управлять фокусом.
  • Логика циклической навигации обеспечивает удобство работы с клавишами вверх/вниз.
  • Обёртывание списка в элемент с tabIndex={0} делает весь список фокусируемым.

Горячие клавиши и комбинации

Для реализации глобальных горячих клавиш применяются слушатели на window или document:

import { useEffect } from "preact/hooks";

export default function Hotkeys() {
  useEffect(() => {
    const handleKey = (e: KeyboardEvent) => {
      if (e.ctrlKey && e.key === "s") {
        e.preventDefault();
        console.log("Сохранение через Ctrl+S");
      }
    };

    window.addEventListener("keydown", handleKey);
    return () => window.removeEventListener("keydown", handleKey);
  }, []);

  return null;
}

Рекомендации:

  • Всегда отменять стандартное поведение (e.preventDefault()), если клавиша перехватывается.
  • Обрабатывать модификаторы (ctrlKey, shiftKey, altKey) для комбинаций.
  • Подключать и удалять слушатели корректно через useEffect для предотвращения утечек памяти.

Доступность и ARIA

Keyboard navigation тесно связана с доступностью. В Fresh применяются стандартные атрибуты ARIA:

  • aria-selected для выбранных элементов.
  • role="menu" и role="menuitem" для меню.
  • aria-activedescendant для указания активного элемента списка.

Пример интеграции ARIA с динамическим меню:

<ul role="menu" aria-activedescendant={`item-${activeIndex}`} tabIndex={0}>
  {items.map((item, i) => (
    <li
      role="menuitem"
      id={`item-${i}`}
      key={i}
      style={{ background: i === activeIndex ? "lightblue" : "transparent" }}
    >
      {item}
    </li>
  ))}
</ul>

Эти атрибуты помогают вспомогательным технологиям корректно идентифицировать текущий фокус и выбранные элементы.


Итоговая структура подхода

  1. События клавиатуры — управление вводом через onKeyDown, onKeyUp.
  2. Фокусировка — работа с ref и tabIndex.
  3. Навигация по элементам — обработка стрелок, Home/End, циклическая навигация.
  4. Горячие клавиши — глобальные сочетания с модификаторами.
  5. Доступность — использование ARIA для вспомогательных технологий.

Сочетание этих техник позволяет создавать интерфейсы, полностью управляемые с клавиатуры, обеспечивая удобство и соответствие стандартам доступности.