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

Клавиатурная навигация является важным аспектом взаимодействия пользователя с веб-приложениями, особенно при работе с компонентами пользовательского интерфейса. Slim.js предоставляет гибкие возможности для организации работы с событиями клавиатуры, поддерживая декларативный и реактивный подход к обновлению интерфейса.

Подключение обработчиков клавиатуры

В Slim.js обработка событий клавиатуры осуществляется через стандартные DOM-события keydown, keyup и keypress. В компонентах Slim.js это можно сделать с помощью директивы @event, которая связывает событие с методом класса компонента:

import { Slim } from 'slim-js';

class KeyboardComponent extends Slim {
  static get observedAttributes() {
    return [];
  }

  constructor() {
    super();
  }

  onKeyDown(event) {
    if (event.key === 'ArrowUp') {
      this.moveUp();
    } else if (event.key === 'ArrowDown') {
      this.moveDown();
    }
  }

  moveUp() {
    console.log('Навигация вверх');
  }

  moveDown() {
    console.log('Навигация вниз');
  }

  get template() {
    return Slim.html`
      <div tabindex="0" @keydown="${this.onKeyDown}">
        Навигация с клавиатуры
      </div>
    `;
  }
}

customElements.define('keyboard-component', KeyboardComponent);

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

  • Элемент должен иметь tabindex, чтобы получать фокус и события клавиатуры.
  • Метод обработчика принимает объект события, который содержит свойства key, code, ctrlKey, altKey и другие.
  • Реакция на клавиши может быть любой — вызов методов, изменение состояния компонента или навигация по списку элементов.

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

Фокусировка элементов — основа клавиатурной навигации. В Slim.js для управления фокусом используется стандартный DOM-метод focus(). В сочетании с реактивными свойствами можно динамически перемещать фокус между элементами:

moveDown() {
  const nextElement = this.querySelector('.item.active')?.nextElementSibling;
  if (nextElement) {
    this.querySelector('.item.active')?.classList.remove('active');
    nextElement.classList.add('active');
    nextElement.focus();
  }
}

Важные аспекты:

  • Управление фокусом позволяет реализовать клавишу Tab и навигацию стрелками.
  • Использование CSS-классов для выделения текущего активного элемента улучшает визуальное восприятие.

Обработка комбинаций клавиш

Slim.js не ограничивает стандартные возможности JavaScript для работы с комбинациями клавиш. Можно проверять модификаторы (Ctrl, Alt, Shift) и вызывать соответствующие методы:

onKeyDown(event) {
  if (event.ctrlKey && event.key === 's') {
    event.preventDefault();
    this.saveDocument();
  }
}

saveDocument() {
  console.log('Документ сохранен');
}

Особенности реализации:

  • Использование event.preventDefault() предотвращает стандартное поведение браузера.
  • Комбинации клавиш позволяют реализовать горячие клавиши и ускоряют работу с интерфейсом.

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

Для полноценной клавиатурной навигации необходимо учитывать доступность компонентов. Использование атрибутов aria-* помогает экранным читалкам корректно интерпретировать состояние элементов:

<div role="menu" aria-orientation="vertical">
  <div role="menuitem" tabindex="0">Пункт 1</div>
  <div role="menuitem" tabindex="-1">Пункт 2</div>
</div>
  • role="menu" определяет контейнер как меню.
  • tabindex="0" делает элемент фокусируемым.
  • Управление фокусом при помощи клавиш стрелок должно обновлять tabindex активного элемента.

Реактивная обработка состояния

Slim.js поддерживает реактивные свойства, что позволяет автоматически обновлять интерфейс при изменении состояния фокусируемого элемента:

static get observedAttributes() {
  return ['active-index'];
}

attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'active-index') {
    this.updateActiveItem(parseInt(newValue));
  }
}

updateActiveItem(index) {
  const items = this.querySelectorAll('.item');
  items.forEach((item, i) => {
    if (i === index) {
      item.classList.add('active');
      item.focus();
    } else {
      item.classList.remove('active');
    }
  });
}

Преимущества реактивного подхода:

  • Автоматическое обновление интерфейса при изменении состояния.
  • Минимизация ручного управления классами и фокусом.
  • Легкая интеграция с другими компонентами Slim.js.

Примеры типовой навигации

  1. Навигация по списку с клавишами ArrowUp и ArrowDown.
  2. Переключение вкладок с помощью ArrowLeft и ArrowRight.
  3. Использование комбинаций клавиш для быстрого доступа к действиям (например, Ctrl+S, Ctrl+Z).

Поддержка событий на уровне документа

Для глобальной клавиатурной навигации можно использовать события на document или window. В Slim.js это также можно делать в методе connectedCallback:

connectedCallback() {
  super.connectedCallback();
  this._onGlobalKeyDown = this.onGlobalKeyDown.bind(this);
  document.addEventListener('keydown', this._onGlobalKeyDown);
}

disconnectedCallback() {
  document.removeEventListener('keydown', this._onGlobalKeyDown);
}

onGlobalKeyDown(event) {
  if (event.key === 'Escape') {
    this.closeMenu();
  }
}
  • Такой подход позволяет реагировать на клавиши вне конкретного компонента.
  • Необходимо обязательно удалять обработчики при удалении компонента, чтобы избежать утечек памяти.

Особенности интеграции с другими компонентами

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