
Для списка выбора сначала оцените, достаточно ли нативного элемента <select>: он уже поддерживает клавиатурную навигацию и доступен вспомогательным технологиям. Самописный компонент оправдан, когда нужны нестандартное оформление или поведение. Тогда заранее определите модель фокуса, состояния открытия и выбора, обработку клавиш и проверку с клавиатуры.
Опорные решения для доступного раскрывающегося списка
- Используйте нативный
<select>, если его возможностей достаточно: не придется самостоятельно воспроизводить стандартную клавиатурную логику. - Для самописного списка выберите одну модель фокуса: перемещать фокус по вариантам или оставлять его на управляющем элементе и менять активный вариант.
- Задайте понятные правила для стрелок, Enter, пробела и Escape, учитывая, открыт список или закрыт.
- Синхронизируйте видимое состояние, атрибуты ARIA и реальное положение фокуса.
- Проверьте сценарии клавиатурой и убедитесь, что после закрытия списка фокус не теряется.
Когда нативный select лучше самописного списка
Нативный <select> подходит для обычного выбора одного значения, когда приемлемы стандартное оформление и поведение браузера. Его можно стилизовать в допустимых пределах и связать с подписью с помощью атрибутов id и for у элемента .
Самописный список стоит делать только при конкретном требовании, которое нельзя разумно выполнить нативным элементом: например, когда интерфейсу нужны особые варианты с дополнительными данными или специфическая компоновка. Создание интерактивных элементов сайта требует самостоятельно реализовать управление клавиатурой, состояние и семантику, поэтому кастомный компонент увеличивает объём проверки.
- Выбирайте нативный элемент, если нужен стандартный выбор значения в форме.
- Рассматривайте кастомный компонент, если требования к интерфейсу оправдывают дополнительную разработку и тестирование.
- Не подменяйте список меню: меню предназначено для команд и навигационных действий, а список выбора - для установки значения.
Как устроить состояние и открытие списка
Для разработки веб-сайтов с пользовательскими элементами сначала зафиксируйте поведение, а затем пишите обработчики. До реализации проверьте требования к выбору, закрытию и возврату фокуса.
- Опишите состояния: закрыт, открыт, активный вариант и выбранное значение. Активный вариант может меняться при перемещении, не меняя выбор до подтверждения.
- Определите поведение при открытии: какой вариант становится активным - выбранный, первый или последний. Выберите правило, соответствующее сценарию интерфейса.
- Назначьте элементу управления доступное имя, а вариантам - корректные роли и связи с контейнером согласно выбранному паттерну.
- Решите, как компонент сообщает новое значение форме и как закрывается после выбора.
- Подготовьте проверку на реальной странице: клавиатура, браузер и используемые вспомогательные технологии могут по-разному взаимодействовать с самописной реализацией.
Выпадающий список HTML CSS JavaScript не становится доступным только благодаря стилям или обработчику клика: визуальные состояния, семантика и клавиатурное поведение должны работать согласованно.
Клавиши стрелок, Enter и пробел: логика перемещения
Перед шагами учтите риски и ограничения:
- Не перехватывайте клавиши, пока фокус находится вне компонента.
- Не допускайте, чтобы фокус уходил в скрытые варианты или терялся при закрытии.
- Не меняйте выбранное значение при каждом перемещении, если подтверждение должно выполняться отдельно.
- Не назначайте одну клавишу на конфликтующие действия без чёткого правила для открытого и закрытого состояния.
-
Определите открытие.
При фокусе на управляющем элементе Enter, пробел или стрелка вниз могут открывать список - выберите и последовательно реализуйте подходящий набор. Если список открылся, установите активный вариант и сообщите состояние через соответствующие атрибуты. -
Перемещайте активный вариант стрелками.
При открытом списке стрелка вниз переводит активность к следующему варианту, стрелка вверх - к предыдущему. Заранее выберите поведение на границах: остановка на первом и последнем пункте либо переход по кругу. -
Подтверждайте выбор.
Enter обычно выбирает активный вариант, обновляет отображаемое значение и закрывает список. Если ваш компонент использует пробел для выбора, проверьте, что клавиша не прокручивает страницу и её назначение понятно пользователю. -
Обработайте переход к началу и концу.
При необходимости Home и End перемещают активность к первому и последнему вариантам. Добавляйте это поведение только при последовательной реализации и проверке. -
Не ломайте ввод текста.
Если компонент поддерживает поиск по первым символам, предусмотрите его отдельно и не перехватывайте клавиши так, чтобы пользователь не мог вводить текст в соседние поля.
Разработка выпадающего меню на JavaScript требует точного различения фокуса и активного пункта. Например, пользователь открывает список стрелкой вниз, перемещается стрелками, выбирает пункт Enter и продолжает работу с предсказуемого элемента.
Фокус, Escape и возврат к элементу управления
Проверьте поведение по этому списку:
- Управляющий элемент доступен клавишей Tab и имеет заметный индикатор фокуса.
- Открытие списка не перемещает фокус в скрытый или невидимый элемент.
- Выбранная и активная опции различаются, если компонент позволяет перемещаться без немедленного выбора.
- Escape закрывает список и возвращает фокус к элементу, который его открыл.
- После выбора фокус остаётся в предсказуемом месте, обычно на управляющем элементе.
- Закрытие по щелчку вне компонента не оставляет фокус в удалённой или скрытой части интерфейса.
- Повторное открытие восстанавливает выбранный вариант или другое заранее определённое начальное положение.
Если выбранная модель перемещает DOM-фокус по вариантам, при закрытии сначала сделайте варианты недоступными для фокуса, затем верните фокус на управляющий элемент. При модели с фокусом на управляющем элементе обновляйте активный вариант так, чтобы вспомогательные технологии могли определить его.
Роли ARIA без дублирования семантики
Частые ошибки при создании самописного компонента:
- Добавлять роли ARIA к нативному
<select>без необходимости и тем самым подменять его встроенную семантику. - Использовать
role="menu"и роли пунктов меню для списка выбора значения. - Задавать роль списка, но не связывать управляющий элемент с раскрываемой областью.
- Оставлять значение
aria-expandedнеизменным при открытии и закрытии. - Сообщать о выбранном пункте атрибутом, который не соответствует применённой модели виджета.
- Одновременно перемещать DOM-фокус и управлять активностью через
aria-activedescendant, не определив, какая модель используется. - Скрывать список только визуально, оставляя его варианты доступными для Tab или вспомогательных технологий.
- Создавать дублирующие доступные имена, например повторять подпись и в тексте управляющего элемента, и в нескольких ARIA-атрибутах.
Для нативного элемента сохраняйте штатную семантику. Для кастомного выберите подходящий паттерн ARIA и реализуйте его целиком: роли, состояние, связь элементов и ожидаемую клавиатурную модель.
Проверка клавиатурного сценария и частые сбои
- Нативный
<select>. Предпочтителен для стандартного выбора, когда достаточно поведения браузера. Проверьте подпись, последовательность Tab и работу выбора в целевых браузерах. - Кастомный одиночный список. Уместен при обоснованных требованиях к внешнему виду или поведению. Сначала реализуйте выбранный паттерн целиком, затем проверяйте стрелки, подтверждение, отмену и фокус.
- Нативный элемент с ограниченной стилизацией. Подходит, если интерфейсу нужна визуальная настройка, но нет причины воспроизводить клавиатурную логику самостоятельно.
Клавиатурная проверка должна охватывать открытие, перемещение, выбор, отмену и продолжение навигации. Доступный выпадающий список с клавиатурной навигацией - это не отдельный набор обработчиков, а согласованная работа фокуса, состояния, семантики и видимого интерфейса.
Разбор спорных случаев при клавиатурной навигации
Нужно ли добавлять JavaScript к обычному select?
Для стандартного выбора обычно нет: браузер уже обеспечивает базовое взаимодействие с клавиатуры. JavaScript нужен только для дополнительной логики приложения, например обработки выбранного значения.
Какая клавиша должна открывать самописный список?
Выберите последовательное поведение для Enter, пробела и стрелок с учётом применяемого паттерна. Важно, чтобы открытие было доступно с клавиатуры, а состояние компонента корректно обновлялось.
Должен ли пробел выбирать вариант?
Это зависит от выбранного паттерна и состояния списка. Опишите поведение явно и проверьте, что пробел не вызывает неожиданную прокрутку или конфликт с вводом.
Нужно ли закрывать список по Escape?
Для открытого самописного списка Escape должен отменять текущий просмотр или закрывать компонент согласно его модели. После закрытия верните фокус на управляющий элемент.
Можно ли оставить DOM-фокус на кнопке?
Да, если компонент использует модель с активным потомком и корректно сообщает активный вариант вспомогательным технологиям. Не смешивайте её с перемещением DOM-фокуса по пунктам без обоснованной реализации.
Почему нельзя использовать роль menu для списка значений?
Меню описывает набор команд, а список выбора - выбор значения. Неверная роль может создать у пользователей вспомогательных технологий ожидания клавиатурного поведения, которым компонент не соответствует.
Что проверять, если после закрытия фокус исчезает?
Убедитесь, что закрытые варианты больше не доступны для фокуса, а обработчик закрытия возвращает фокус к элементу управления. Проверьте это после выбора, Escape и закрытия по щелчку вне компонента.
Автор: Виктор Егоров.