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

FAST Element предоставляет гибкие возможности для организации клавиатурной навигации в веб-компонентах, обеспечивая удобный доступ и управление фокусом с помощью клавиш. Основная цель — сделать интерфейсы доступными и предсказуемыми при использовании клавиатуры.

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

Фокус в FAST Element контролируется через стандартные атрибуты HTML (tabindex) и встроенные методы компонента. Для управления фокусируемыми элементами используются следующие механизмы:

  • tabindex — определяет порядок обхода элементов при навигации клавишей Tab.

    • tabindex="0" делает элемент фокусируемым в естественном порядке DOM.
    • tabindex="-1" делает элемент фокусируемым программно, но исключает его из последовательности обхода клавишей Tab.
  • Методы компонента:

    • focus() — программно устанавливает фокус на элемент.
    • blur() — снимает фокус.

Пример управления фокусом в кастомном компоненте:

import { FASTElement, html, css } from "@microsoft/fast-element";

class MyButton extends FASTElement {
    template = html`<button @click="${x => x.handleClick()}">Нажми</button>`;
    
    handleClick() {
        console.log("Кнопка нажата");
    }
}

FASTElement.define({ name: 'my-button', template: MyButton.template, styles: css`` });

Фокус автоматически устанавливается на кнопку при взаимодействии через клавиатуру.

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

FAST Element поддерживает обработку стандартных событий клавиатуры: keydown, keyup, keypress. Основное применение — создание логики, реагирующей на навигацию и действия пользователя:

  • keydown — вызывается при нажатии клавиши, до выполнения действия по умолчанию.
  • keyup — вызывается при отпускании клавиши.
  • keypress — устаревший, используется редко, предпочтительно keydown.

Пример обработки клавиш для навигации:

this.addEventListener("keydown", (e) => {
    switch (e.key) {
        case "ArrowDown":
            this.moveFocusNext();
            e.preventDefault();
            break;
        case "ArrowUp":
            this.moveFocusPrevious();
            e.preventDefault();
            break;
    }
});

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

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

getFocusableElements() {
    return Array.from(this.shadowRoot.querySelectorAll('[tabindex="0"]'));
}

moveFocusNext() {
    const elements = this.getFocusableElements();
    const currentIndex = elements.indexOf(this.shadowRoot.activeElement);
    const nextIndex = (currentIndex + 1) % elements.length;
    elements[nextIndex].focus();
}

moveFocusPrevious() {
    const elements = this.getFocusableElements();
    const currentIndex = elements.indexOf(this.shadowRoot.activeElement);
    const prevIndex = (currentIndex - 1 + elements.length) % elements.length;
    elements[prevIndex].focus();
}

Такая реализация обеспечивает циклическую навигацию с клавиатуры по элементам внутри компонента.

Использование FASTElement и FocusController

FAST Element предлагает FocusController для централизованного управления фокусом в компонентах с большим количеством интерактивных элементов. Основные возможности:

  • Автоматическое сохранение и восстановление фокуса.
  • Управление последовательностью обхода при помощи roving tabindex.
  • Обработка событий клавиатуры с минимальным кодом.

Пример интеграции FocusController:

import { FASTElement, FocusController } from "@microsoft/fast-element";

class MenuList extends FASTElement {
    focusController = new FocusController(this);

    connectedCallback() {
        super.connectedCallback();
        this.focusController.register();
    }
}

Контроллер позволяет автоматически реализовать шаблон roving tabindex, когда один элемент получает tabindex="0", а все остальные — -1, что обеспечивает последовательный обход клавишей Tab.

Ровинг tabindex

Ровинг tabindex — стандартная практика для сложных компонентов, таких как меню, вкладки, списки. Суть:

  1. Только один элемент получает tabindex="0" — текущий активный.
  2. Остальные элементы имеют tabindex="-1".
  3. При навигации стрелками фокус перемещается, а tabindex пересчитывается.

Пример применения в компоненте вкладок:

moveFocusTo(index) {
    const tabs = this.shadowRoot.querySelectorAll('[role="tab"]');
    tabs.forEach((tab, i) => tab.tabIndex = i === index ? 0 : -1);
    tabs[index].focus();
}

Это обеспечивает логичное и предсказуемое поведение при работе с клавиатурой.

Дополнительные возможности

  • Обработка комбинаций клавиш: FAST Element позволяет слушать сочетания Ctrl+Arrow, Shift+Tab и другие, что полезно для сложных интерфейсов.
  • Интеграция с ARIA: установка role, aria-selected, aria-expanded совместно с фокусом делает навигацию доступной для экранных читалок.
  • События кастомного фокуса: можно создать события focus-moved или item-selected, чтобы другие компоненты реагировали на изменение фокуса.

Практические рекомендации

  • Всегда использовать tabindex для контроля последовательности обхода.
  • Применять роving tabindex для групп элементов, где важно управлять активным элементом.
  • Централизовать обработку клавиатуры через контроллер или методы компонента, чтобы избегать разрозненных обработчиков.
  • Обеспечивать поддержку ARIA-атрибутов, чтобы клавиатурная навигация была доступной.

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