Lit предоставляет удобные инструменты для создания веб-компонентов с управлением состоянием и реактивными свойствами. Одной из важных задач при разработке интерактивных компонентов является поддержка навигации с клавиатуры для обеспечения доступности и удобства использования.
В Lit обработка клавиатуры реализуется через стандартные DOM-события:
keydown, keyup и keypress.
Наиболее часто используется keydown, поскольку оно
срабатывает при нажатии клавиши и позволяет предотвращать стандартное
поведение браузера.
Пример простого обработчика:
import { LitElement, html, css } from 'lit';
class KeyboardNavExample extends LitElement {
static styles = css`
.item {
padding: 8px;
cursor: pointer;
}
.item:focus {
outline: 2px solid blue;
}
`;
render() {
return html`
<div
class="item"
tabindex="0"
@keydown="${this._handleKeyDown}"
>
Элемент для навигации
</div>
`;
}
_handleKeyDown(event) {
switch (event.key) {
case 'ArrowUp':
console.log('Вверх');
break;
case 'ArrowDown':
console.log('Вниз');
break;
case 'Enter':
console.log('Активирован элемент');
break;
}
}
}
customElements.define('keyboard-nav-example', KeyboardNavExample);
Ключевые моменты:
tabindex="0" делает элемент доступным для
фокусировки клавишей Tab.@keydown получает объект события с ключевым
свойством key, определяющим нажатую клавишу.switch позволяет легко добавлять
обработку различных клавиш.Для навигации между элементами необходимо уметь управлять фокусом. В
Lit это делается через стандартный DOM-метод focus():
this.shadowRoot.querySelectorAll('.item')[index].focus();
В комбинации с обработкой клавиш ArrowUp и
ArrowDown можно перемещать фокус по списку элементов:
_handleKeyDown(event) {
const items = Array.from(this.shadowRoot.querySelectorAll('.item'));
const currentIndex = items.indexOf(this.shadowRoot.activeElement);
if (event.key === 'ArrowDown' && currentIndex < items.length - 1) {
items[currentIndex + 1].focus();
event.preventDefault();
}
if (event.key === 'ArrowUp' && currentIndex > 0) {
items[currentIndex - 1].focus();
event.preventDefault();
}
}
Особенности реализации:
activeElement в shadow DOM возвращает текущий элемент с
фокусом.event.preventDefault() предотвращает стандартное
поведение, например прокрутку страницы стрелками.Array.from) облегчает
поиск индекса текущего элемента.Элементы интерфейса должны реагировать на клавиши Enter
и Space так же, как на клик мыши. Это повышает
доступность:
_handleKeyDown(event) {
if (event.key === 'Enter' || event.key === ' ') {
this._activateElement(event.target);
event.preventDefault();
}
}
_activateElement(element) {
console.log(`Элемент ${element.textContent} активирован`);
}
Важные моменты:
Space нужно использовать
event.preventDefault(), иначе страница прокрутится.Для компонентов, таких как меню, списки или вкладки, часто требуется поддержка более сложной навигации:
role="menu" или role="tablist" и
aria-* атрибутов для улучшения совместимости с экранными
читалками.Ctrl + ArrowLeft/ArrowRight для перехода между вкладками
или секциями.Пример циклической навигации по списку:
_handleKeyDown(event) {
const items = Array.from(this.shadowRoot.querySelectorAll('.item'));
let currentIndex = items.indexOf(this.shadowRoot.activeElement);
if (event.key === 'ArrowDown') {
currentIndex = (currentIndex + 1) % items.length;
items[currentIndex].focus();
event.preventDefault();
}
if (event.key === 'ArrowUp') {
currentIndex = (currentIndex - 1 + items.length) % items.length;
items[currentIndex].focus();
event.preventDefault();
}
}
tabindex="0" или семантические элементы
<button>, <a>).aria-* атрибуты.Эти практики обеспечивают удобную и доступную навигацию с клавиатуры для веб-компонентов, созданных с помощью Lit.