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 для предотвращения утечек памяти.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>
Эти атрибуты помогают вспомогательным технологиям корректно идентифицировать текущий фокус и выбранные элементы.
onKeyDown, onKeyUp.ref и
tabIndex.Сочетание этих техник позволяет создавать интерфейсы, полностью управляемые с клавиатуры, обеспечивая удобство и соответствие стандартам доступности.