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

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


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

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

Пример:

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

class KeyboardExample extends Component {
  constructor(props) {
    super(props);
    this.state = { message: '' };
  }

  handleKeyDown = (event) => {
    switch(event.key) {
      case 'ArrowUp':
        this.setState({ message: 'Нажата стрелка вверх' });
        break;
      case 'ArrowDown':
        this.setState({ message: 'Нажата стрелка вниз' });
        break;
      case 'Enter':
        this.setState({ message: 'Нажат Enter' });
        break;
      default:
        this.setState({ message: `Нажата клавиша ${event.key}` });
    }
  }

  render() {
    return (
      <div tabIndex="0" onKeyD own={this.handleKeyDown} style={{ outline: 'none' }}>
        <p>{this.state.message}</p>
        <p>Фокус на этом блоке и используйте клавиши стрелок или Enter</p>
      </div>
    );
  }
}

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

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

  • Атрибут tabIndex="0" делает элемент фокусируемым.
  • Использование style={{ outline: 'none' }} убирает стандартное обрамление фокуса, чтобы стилизовать его по своему усмотрению.
  • События передаются компоненту напрямую через пропсы.

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

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

class FocusExample extends Component {
  constructor(props) {
    super(props);
    this.inputRef = null;
  }

  setInputRef = (el) => {
    this.inputRef = el;
  }

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

  render() {
    return (
      <div>
        <input ref={this.setInputRef} type="text" placeholder="Введите текст" />
        <button onCl ick={this.focusInput}>Сфокусировать input</button>
      </div>
    );
  }
}

Особенности работы с фокусом:

  • Метод focus() позволяет программно перемещать фокус.
  • Использование рефов упрощает доступ к DOM-элементам внутри классовых компонентов.
  • Для функциональных компонентов используется createRef и useRef при интеграции с хуками.

Обработка последовательностей клавиш

Иногда требуется реагировать на комбинации клавиш, например, Ctrl+S или Alt+Arrow. Это реализуется через проверку модификаторов event.ctrlKey, event.altKey и event.shiftKey.

handleKeyCombination = (event) => {
  if (event.ctrlKey && event.key === 's') {
    event.preventDefault();
    console.log('Сохранение документа');
  }

  if (event.altKey && event.key === 'ArrowRight') {
    console.log('Переход вперед');
  }
}

Замечания по обработке комбинаций:

  • Использование event.preventDefault() предотвращает стандартное поведение браузера.
  • Все модификаторы доступны через объект события.
  • Важно корректно обрабатывать регистр букв (event.key чувствителен к Shift).

Виртуальные клавиатурные схемы

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

Пример реализации навигации в таблице:

class TableNavigation extends Component {
  constructor(props) {
    super(props);
    this.state = { selectedRow: 0, selectedCol: 0 };
  }

  handleKeyDown = (event) => {
    const { selectedRow, selectedCol } = this.state;
    const rowCount = this.props.data.length;
    const colCount = this.props.data[0].length;

    switch(event.key) {
      case 'ArrowUp':
        this.setState({ selectedRow: Math.max(selectedRow - 1, 0) });
        break;
      case 'ArrowDown':
        this.setState({ selectedRow: Math.min(selectedRow + 1, rowCount - 1) });
        break;
      case 'ArrowLeft':
        this.setState({ selectedCol: Math.max(selectedCol - 1, 0) });
        break;
      case 'ArrowRight':
        this.setState({ selectedCol: Math.min(selectedCol + 1, colCount - 1) });
        break;
    }
  }

  render() {
    const { data } = this.props;
    const { selectedRow, selectedCol } = this.state;

    return (
      <table tabIndex="0" onKeyD own={this.handleKeyDown} style={{ outline: 'none' }}>
        <tbody>
          {data.map((row, rIdx) => (
            <tr key={rIdx}>
              {row.map((cell, cIdx) => (
                <td key={cIdx} style={{ background: rIdx === selectedRow && cIdx === selectedCol ? '#ccc' : 'transparent' }}>
                  {cell}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    );
  }
}

Особенности виртуальных схем:

  • Хранение состояния выбранного элемента в компоненте.
  • Изменение состояния через события клавиатуры.
  • Визуальная индикация текущего фокуса через стили.

Поддержка доступности

Для обеспечения доступности необходимо использовать:

  • Атрибуты aria-selected, aria-activedescendant и role для обозначения текущего состояния элементов.
  • Правильное управление фокусом через tabIndex.
  • Поддержку стандартных комбинаций клавиш для элементов списка, меню и таблиц.

Пример с атрибутами ARIA:

<td
  role="gridcell"
  aria-selected={rIdx === selectedRow && cIdx === selectedCol}
  tabIndex={rIdx === selectedRow && cIdx === selectedCol ? 0 : -1}
>
  {cell}
</td>

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


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