Клавиатурная навигация и доступность

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

В Inferno работа с клавиатурой строится на стандартных DOM-событиях: onKeyDown, onKeyUp и onKeyPress. Отличие фреймворка заключается в высокой производительности при обработке большого количества компонентов и минимальных задержках при изменении состояния компонентов, что особенно важно для интерактивных элементов, таких как формы, таблицы и навигационные меню.

import { render } from 'inferno';
import { Component } from 'inferno-component';

class KeyboardComponent extends Component {
    handleKeyDown = (event) => {
        switch(event.key) {
            case 'Enter':
                console.log('Нажата клавиша Enter');
                break;
            case 'ArrowUp':
                console.log('Стрелка вверх');
                break;
            case 'ArrowDown':
                console.log('Стрелка вниз');
                break;
        }
    }

    render() {
        return (
            <div tabIndex={0} onKeyD own={this.handleKeyDown}>
                Фокусируемый элемент
            </div>
        );
    }
}

render(<KeyboardComponent />, document.getElementById('app'));

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

  • tabIndex={0} делает элемент фокусируемым с клавиатуры.
  • Обработчики событий клавиатуры позволяют перехватывать действия и реализовывать кастомную логику навигации.
  • Использование switch или условных конструкций позволяет эффективно обрабатывать разные клавиши.

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

Для обеспечения корректной клавиатурной навигации необходимо грамотно управлять фокусом. Inferno не предоставляет встроенных хуков, аналогичных React useRef в функциональных компонентах, однако в классовых компонентах можно использовать прямые ссылки на DOM-элементы через ref.

class FocusComponent extends Component {
    inputRef = null;

    setFocus = () => {
        if (this.inputRef) {
            this.inputRef.focus();
        }
    }

    render() {
        return (
            <div>
                <input ref={el => this.inputRef = el} type="text" />
                <button onCl ick={this.setFocus}>Установить фокус</button>
            </div>
        );
    }
}

Важные моменты:

  • ref сохраняет ссылку на DOM-элемент для последующего управления.
  • Фокус можно программно устанавливать через метод focus().
  • Управление фокусом важно для модальных окон, динамических списков и интерактивных элементов.

Обеспечение доступности (ARIA)

Для поддержки пользователей с ограниченными возможностями необходимо применять ARIA-атрибуты. Inferno позволяет напрямую добавлять эти атрибуты к компонентам без дополнительного оборачивания.

<div role="menu" aria-label="Главное меню">
    <button role="menuitem" tabIndex={0}>Пункт 1</button>
    <button role="menuitem" tabIndex={0}>Пункт 2</button>
</div>

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

  • role определяет семантику элемента для вспомогательных технологий.
  • aria-label задаёт описание элемента, которое озвучивается экранными читалками.
  • tabIndex обеспечивает последовательность перехода по клавиатуре.

Навигация внутри сложных компонентов

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

class ListNavigation extends Component {
    state = { focusedIndex: 0 };

    handleKeyDown = (event) => {
        const { focusedIndex } = this.state;
        const maxIndex = this.items.length - 1;

        if(event.key === 'ArrowDown') {
            this.setState({ focusedIndex: focusedIndex === maxIndex ? 0 : focusedIndex + 1 });
        }

        if(event.key === 'ArrowUp') {
            this.setState({ focusedIndex: focusedIndex === 0 ? maxIndex : focusedIndex - 1 });
        }
    }

    items = ['Элемент 1', 'Элемент 2', 'Элемент 3'];

    render() {
        return (
            <ul tabIndex={0} onKeyD own={this.handleKeyDown}>
                {this.items.map((item, index) => (
                    <li key={index} className={this.state.focusedIndex === index ? 'focused' : ''}>
                        {item}
                    </li>
                ))}
            </ul>
        );
    }
}

Особенности:

  • Циклическая навигация позволяет пользователю перемещаться по элементам без выхода из компонента.
  • Состояние focusedIndex управляет визуальной подсветкой и логикой фокуса.
  • В реальных приложениях важно синхронизировать фокус с DOM через ref для корректного взаимодействия с экранными читалками.

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

Inferno позволяет реализовывать кастомные сочетания клавиш, что полезно для ускоренного взаимодействия с приложением.

handleShortcut = (event) => {
    if(event.ctrlKey && event.key === 's') {
        event.preventDefault();
        console.log('Сохранение через Ctrl+S');
    }
}

Особенности:

  • event.ctrlKey, event.altKey, event.shiftKey помогают отслеживать модификаторы.
  • preventDefault() предотвращает стандартное действие браузера.
  • Возможность создавать глобальные и локальные шорткаты повышает удобство и скорость работы интерфейса.

Визуальные подсказки фокуса

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

<li className={this.state.focusedIndex === index ? 'focused' : ''}>

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

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

Совместимость с мобильными устройствами

Хотя клавиатурная навигация ориентирована на десктопы, важно сохранять совместимость с сенсорными экранами:

  • Элементы с tabIndex остаются доступными для фокусировки при использовании внешней клавиатуры.
  • ARIA-атрибуты помогают вспомогательным технологиям корректно распознавать интерфейс.
  • Комбинация клавиатурной навигации и сенсорного управления обеспечивает универсальность интерфейса.