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

Навигация с клавиатуры — критически важный аспект доступности веб-приложений. В Preact реализация клавиатурной навигации требует понимания событий клавиатуры, управления фокусом и правильной структуры компонентов. Preact предоставляет легковесный API, позволяющий эффективно работать с этими задачами без дополнительных библиотек.


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

В Preact события клавиатуры обрабатываются через стандартные обработчики, аналогичные React:

function handleKeyDown(event) {
    if (event.key === 'ArrowDown') {
        console.log('Пользователь нажал стрелку вниз');
    }
}

Подключение обработчика к элементу осуществляется через атрибут onKeyDown:

<button onKeyD own={handleKeyDown}>Нажми меня</button>

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

  • Preact использует собственную реализацию синтетических событий, но API почти идентичен React.
  • События клавиатуры включают onKeyDown, onKeyUp и onKeyPress. Рекомендуется использовать onKeyDown для обработки навигации, так как оно срабатывает при нажатии клавиши, до её отпускания.
  • event.key обеспечивает кроссбраузерную совместимость, в отличие от устаревшего event.keyCode.

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

Фокус — основной инструмент навигации с клавиатуры. В Preact управление фокусом производится через рефы:

import { h } from 'preact';
import { useRef } from 'preact/hooks';

function FocusableInput() {
    const inputRef = useRef(null);

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

    return (
        <div>
            <input ref={inputRef} type="text" />
            <button onCl ick={focusInput}>Сфокусировать поле</button>
        </div>
    );
}

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

  • useRef возвращает объект с полем current, указывающим на DOM-элемент.
  • Метод .focus() позволяет программно установить фокус на элемент.
  • Для элементов <div> или <span> можно использовать tabIndex={0}, чтобы сделать их фокусируемыми.

Табуляция и порядок фокуса

Порядок перемещения фокуса с клавишей Tab определяется естественным потоком документа и атрибутом tabIndex:

<div tabIndex={0}>Элемент 1</div>
<div tabIndex={0}>Элемент 2</div>
<div tabIndex={0}>Элемент 3</div>

Особенности использования tabIndex:

  • tabIndex={0} — элемент доступен в естественном порядке табуляции.
  • tabIndex={-1} — элемент фокусируемый программно, но пропускается при обычной навигации Tab.
  • tabIndex={>0} — задаёт явный порядок обхода. Используется редко, так как может усложнять поддерживаемость.

Навигация между компонентами

В крупных приложениях часто требуется перемещаться между компонентами, например, списками или меню. Для этого применяются массивы рефов:

import { useRef } from 'preact/hooks';

function Menu() {
    const itemsRef = useRef([]);

    const handleKeyDown = (index) => (event) => {
        if (event.key === 'ArrowDown') {
            const nextIndex = (index + 1) % itemsRef.current.length;
            itemsRef.current[nextIndex].focus();
        }
        if (event.key === 'ArrowUp') {
            const prevIndex = (index - 1 + itemsRef.current.length) % itemsRef.current.length;
            itemsRef.current[prevIndex].focus();
        }
    };

    return (
        <ul>
            {['Пункт 1', 'Пункт 2', 'Пункт 3'].map((item, i) => (
                <li
                    key={i}
                    tabIndex={0}
                    ref={el => itemsRef.current[i] = el}
                    onKeyD own={handleKeyDown(i)}
                >
                    {item}
                </li>
            ))}
        </ul>
    );
}

Особенности данного подхода:

  • Используется массив рефов для прямого управления фокусом между элементами.
  • Обработчики событий создаются через замыкания, чтобы знать индекс текущего элемента.
  • Применяется циклическая навигация с помощью операторов % и сложения с длиной массива.

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

Для правильной навигации с клавиатуры важно учитывать ARIA-атрибуты:

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

Пример интеграции с Preact:

<li
    role="menuitem"
    aria-selected={selectedIndex === i}
    tabIndex={0}
    onKeyD own={handleKeyDown(i)}
>
    {item}
</li>

Принципы работы:

  • Screen reader корректно воспринимает фокусируемые элементы с ARIA.
  • Использование ARIA улучшает совместимость с клавиатурной навигацией для людей с ограниченными возможностями.

Обработка сочетаний клавиш

Для сложной навигации часто требуется реагировать на комбинации клавиш:

function handleKeyCombination(event) {
    if (event.ctrlKey && event.key === 'ArrowRight') {
        console.log('Переход к следующему разделу');
    }
}

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

  • Проверять модификаторы ctrlKey, shiftKey, altKey или metaKey.
  • Не блокировать стандартные действия браузера без необходимости.
  • Комбинации клавиш лучше документировать для пользователей.

Итоговая структура для навигации с клавиатуры в Preact

  1. Использовать onKeyDown для обработки нажатий.
  2. Применять useRef и .focus() для управления фокусом.
  3. Присваивать tabIndex элементам для правильного обхода.
  4. Строить массивы рефов для комплексной навигации.
  5. Интегрировать ARIA-атрибуты для доступности.
  6. Обрабатывать сочетания клавиш для продвинутых сценариев.

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