Навигация с клавиатуры

Библиотека Slim Select предоставляет полноценную поддержку управления компонентом без использования мыши. Навигация с клавиатуры особенно важна для:

  • доступности интерфейсов;
  • быстрого ввода данных;
  • соответствия требованиям WCAG;
  • работы экранных дикторов;
  • удобства пользователей ноутбуков и терминальных сред.

Slim Select заменяет стандартный <select> собственным DOM-компонентом, поэтому обработка клавиатуры реализуется внутри библиотеки через систему событий и управления фокусом.


Получение фокуса

После инициализации Slim Select создаёт собственный контейнер:

<select id="countries">
  <option value="kz">Казахстан</option>
  <option value="ru">Россия</option>
  <option value="uz">Узбекистан</option>
</select>
new SlimSelect({
  select: '#countries'
})

Переход к компоненту осуществляется стандартной клавишей:

Tab

Когда элемент получает фокус:

  • появляется визуальное выделение;
  • активируется клавиатурное управление;
  • становится доступным открытие списка;
  • запускаются обработчики focus.

Открытие списка с клавиатуры

Slim Select поддерживает несколько способов открытия выпадающего списка.

Клавиша Enter

Enter

Открывает список и переводит фокус к текущему выбранному элементу.

Клавиша Space

Space

Работает аналогично Enter.

Стрелка вниз

ArrowDown

Если список закрыт — открывает его.


Перемещение по элементам списка

После открытия выпадающего меню используются клавиши стрелок.

Переход вниз

ArrowDown

Переход к следующему элементу.

Переход вверх

ArrowUp

Переход к предыдущему элементу.

Пример поведения:

new SlimSelect({
  select: '#countries'
})

При нажатии:

ArrowDown
ArrowDown

активируется третий элемент списка.


Выбор элемента с клавиатуры

Для подтверждения выбора используется:

Enter

После нажатия:

  • выбранный пункт становится активным;
  • значение записывается в исходный <select>;
  • генерируется событие change;
  • список закрывается (в single select).

Закрытие выпадающего списка

Escape

Escape

Закрывает список без изменения значения.

Tab

Tab

Закрывает список и переводит фокус к следующему элементу формы.


Навигация в multiple select

В режиме множественного выбора клавиатурное управление становится сложнее.

Пример:

<select id="tags" multiple>
  <option>JavaScript</option>
  <option>TypeScript</option>
  <option>Node.js</option>
  <option>React</option>
</select>
new SlimSelect({
  select: '#tags'
})

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

  • Enter добавляет элемент;
  • список не закрывается после выбора;
  • стрелки продолжают навигацию;
  • выбранные элементы отображаются как отдельные теги.

Удаление выбранных элементов

В multiple-режиме поддерживается удаление через клавиатуру.

Backspace

Если строка поиска пустая:

Backspace

удаляет последний выбранный тег.

Пример поведения:

[JavaScript] [React]
                 ^
             удаляется

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

Если включён поиск:

new SlimSelect({
  select: '#countries',
  settings: {
    searchText: 'Поиск...'
  }
})

после открытия списка курсор автоматически помещается в поле поиска.

Доступны стандартные клавиши:

  • ArrowLeft
  • ArrowRight
  • Home
  • End
  • Backspace
  • Delete

Автоматическая фильтрация при вводе

Slim Select реагирует на ввод с клавиатуры в реальном времени.

Пример:

<select id="cities">
  <option>Алматы</option>
  <option>Астана</option>
  <option>Караганда</option>
  <option>Шымкент</option>
</select>

При вводе:

ка

отобразится:

Караганда

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

Slim Select позволяет подключать собственные обработчики.

Пример перехвата Enter:

const select = new SlimSelect({
  select: '#countries'
})

document.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') {
    console.log('Нажат Enter')
  }
})

Использование событий focus и blur

Навигация с клавиатуры тесно связана с управлением фокусом.

focus

const element = document.querySelector('#countries')

element.addEventListener('focus', () => {
  console.log('Фокус получен')
})

blur

element.addEventListener('blur', () => {
  console.log('Фокус потерян')
})

Поддержка tabindex

Slim Select наследует поведение стандартного select.

Пример:

<select id="priority" tabindex="3">
  <option>Low</option>
  <option>Medium</option>
  <option>High</option>
</select>

Порядок обхода элементов через Tab определяется значением tabindex.


Отключение элемента из клавиатурной навигации

<select id="hidden-select" tabindex="-1">
  <option>One</option>
  <option>Two</option>
</select>

Такой элемент:

  • не получает фокус через Tab;
  • остаётся доступным программно;
  • может открываться вручную через JavaScript.

Работа с disabled-состоянием

<select id="status" disabled>
  <option>Active</option>
  <option>Inactive</option>
</select>

Поведение:

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

Навигация между группами option

Slim Select поддерживает <optgroup>.

Пример:

<select id="frameworks">
  <optgroup label="Frontend">
    <option>React</option>
    <option>Vue</option>
  </optgroup>

  <optgroup label="Backend">
    <option>Node.js</option>
    <option>Laravel</option>
  </optgroup>
</select>

При навигации стрелками:

  • заголовки групп пропускаются;
  • фокус переходит только по option;
  • структура групп сохраняется визуально.

Поддержка Home и End

Некоторые версии Slim Select поддерживают дополнительные клавиши навигации.

Home

Home

Переход к первому элементу списка.

End

End

Переход к последнему элементу.


Навигация при динамической загрузке данных

Если данные загружаются асинхронно:

new SlimSelect({
  select: '#users',
  events: {
    search: async (search, currentData) => {
      const response = await fetch('/users?q=' + search)
      return response.json()
    }
  }
})

важно учитывать:

  • фокус не должен теряться;
  • клавиатурная навигация должна сохраняться;
  • после обновления данных активный индекс пересчитывается.

Проблемы потери фокуса

Типичная ошибка:

select.setData(newData)

После полной перерисовки компонент может потерять:

  • текущий элемент;
  • позицию курсора;
  • активный индекс;
  • состояние поиска.

Распространённое решение:

const active = document.activeElement
select.setData(data)
active.focus()

Перехват клавиш внутри модальных окон

При использовании Slim Select внутри модальных окон возможен конфликт клавиатурных событий.

Пример:

modal.addEventListener('keydown', (event) => {
  if (event.key === 'Escape') {
    event.stopPropagation()
  }
})

Без контроля событий:

  • Escape может закрывать модальное окно вместо select;
  • Tab может нарушать focus trap;
  • стрелки могут перехватываться контейнером.

Accessibility и ARIA-навигация

Slim Select добавляет ARIA-атрибуты для корректной клавиатурной работы.

Часто используются:

aria-expanded="true"
aria-selected="true"
aria-disabled="false"
role="listbox"
role="option"

Это обеспечивает:

  • поддержку screen reader;
  • озвучивание состояния списка;
  • корректную работу клавиатуры;
  • доступность для пользователей с ограничениями.

Визуальный фокус

Правильное отображение фокуса критично для клавиатурной навигации.

Пример стилизации:

.ss-main:focus {
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59,130,246,.3);
}

Без визуального выделения:

  • пользователь теряет ориентацию;
  • навигация становится неудобной;
  • ухудшается accessibility.

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

Slim Select можно активировать вручную.

Пример:

const slim = new SlimSelect({
  select: '#countries'
})

slim.select.main.focus()

Открытие списка:

slim.open()

Закрытие:

slim.close()

Навигация в длинных списках

При большом количестве элементов клавиатурная навигация должна сопровождаться автоматической прокруткой.

Slim Select автоматически:

  • прокручивает список;
  • удерживает активный элемент в области видимости;
  • обновляет scroll position.

Пример:

new SlimSelect({
  select: '#big-list'
})

для списка из сотен элементов остаётся полностью управляемым с клавиатуры.


Комбинация с формами

При отправке формы клавиатурная навигация должна оставаться предсказуемой.

Пример:

<form>
  <input type="text">

  <select id="category">
    <option>A</option>
    <option>B</option>
  </select>

  <button type="submit">Send</button>
</form>

Стандартный порядок:

Tab → input
Tab → Slim Select
Tab → button
Enter → submit

Предотвращение конфликтов клавиш

Иногда требуется отключить стандартное поведение.

Пример:

document.addEventListener('keydown', (event) => {
  if (event.key === 'ArrowDown') {
    event.preventDefault()
  }
})

Использовать глобальный перехват следует осторожно, поскольку он способен:

  • ломать навигацию Slim Select;
  • нарушать accessibility;
  • блокировать работу screen reader.

Проверка клавиатурной доступности

При тестировании проверяются:

  • доступность через Tab;
  • открытие Enter и Space;
  • закрытие Escape;
  • работа стрелок;
  • корректный focus state;
  • навигация без мыши;
  • взаимодействие со screen reader.

Типичный сценарий тестирования:

Tab
Enter
ArrowDown
ArrowDown
Enter
Tab

Такой набор действий покрывает основную клавиатурную механику компонента.