От автора: в моей работе в качестве консультанта по доступности я часто сталкиваюсь с проблемами на сайтах. В последнее время очень часто встречается то, что люди создают элементы выбора для форм. Я могу сказать, что они пытаются сделать их доступными, потому что добавляют атрибуты ARIA или визуально скрытые инструкции для пользователей программ чтения с экрана. Иногда они используют плагин, создатели которого утверждают, что он доступен. И это здорово, мне нравится, что люди хотят поступать правильно! Но до сих пор я ни разу не сталкивалась с пользовательским элементом выбора, который на самом деле соответствует всем критериям WCAG AA.
Часто я рекомендую людям использовать select вместо этого пользовательского элемента HTML. Да, они супер уродливые, но их гораздо проще стилизовать, чем раньше. Они характеризуются многими функциями доступности — распознаются и четко объявляются всеми программами для чтения с экрана, они надежно и предсказуемо работают с клавиатурой и сенсорным экраном и хорошо смотрятся в высококонтрастных темах.
Но иногда я не могу рекомендовать элемент select в качестве замены. Мы хотим, чтобы кто-то мог выбрать элемент из списка вариантов, но это сложнее, чем кажется. Нам нужно автозаполнение вариантов. Мы хотим поместить туда изображения, а не просто текст. Элемент optgroup некрасив, его трудно стилизовать, и он не объявляется программами для чтения с экрана. Стили фокуса малоконтрастны. Я возлагала большие надежды на элемент datalist, но хотя он хорошо работает с программами для чтения с экрана, он не подходит для людей с плохим зрением, которые масштабируют окно или используют высококонтрастные темы.
Рисунок 1: datalist с увеличением 300%
Элемент select ограничен во многих отношениях. С ними сложно работать, когда у вас есть то, что выглядит почти так, как вы хотите, но слишком ограничено, чтобы быть полезным. Мы знаем, что можем добиться большего, поэтому мы создаем пользовательские элементы.
Давайте разберемся, как это сделать, сохранив все специальные возможности оригинала.
Семантический HTML
Мы начнем с семантической базы HTML. По сути select — это элемент ввода текста, который ограничивает возможные ответы, поэтому давайте создадим стандартный элемент.
<label for="custom-select">User Type</label> <input type="text" id="custom-select">
Затем нам нужно показать всем, кто может видеть, что существуют доступные варианты, поэтому давайте добавим изображение со стрелкой, как в нативном элементе.
<label for="custom-select">User Type</label> <input type="text" id="custom-select"> <img src="arrow-down.svg" alt="">
Для этого элемента мы будем использовать атрибуты ARIA для представления информации в иконке, поэтому мы зададим пустой атрибут alt, чтобы программы чтения с экрана не объявляли имя файла.
Наконец, нам нужен список вариантов. Элемент неупорядоченного списка — разумный выбор. Это также позволяет программам для чтения с экрана понимать, что эти фрагменты текста связаны друг с другом как часть группы.
<ul class="custom-select-options"> <li>User</li> <li>Author</li> <li>Editor</li> <li>Manager</li> <li>Administrator</li> </ul>
Вы можете динамически добавлять или удалять варианты из этого списка, когда вам нужно. И, в отличие от элемента option внутри select, мы можем добавить внутри элемента списка все, что захотим. Так что, если вам нужны изображения, чтобы различать множество объектов с очень похожими именами или добавлять дополнительные детали, вы можете пойти дальше. Я собираюсь добавить дополнительный текст, чтобы помочь объяснить различия между вариантами.
Это хорошая база для начала. Но это не похоже на элемент select! Мы хотим, чтобы наши зрячие пользователи получили что-то, с чем они уже знакомы и знают, как это использовать.
Стилизация с помощью CSS
Я добавлю несколько основных стилей, похожих на те, что описаны в статье Скотта Джела выше.
Мы также должны обеспечить, чтобы люди, которые настраивают цвета в высококонтрастных режимах, по прежнему могли сказать, на что они смотрят. После проверки этого в стандартной высококонтрастной теме Windows я решила добавить левую рамку к стилям фокуса и наведения, чтобы было ясно, какой элемент будет выбран.
Сейчас также можно добавить стили темного режима. Люди, которые испытывают мигрени от ярких экранов, будут вам благодарны!
JavaScript для поведения
Конечно, наш обычай select фактически не делает еще ничего. Для этого у нас есть несколько задач: переключать список вариантов, открывать и закрывать элемент, когда мы кликаем по нему, фильтровать варианты, когда люди вводят данные, и выбирать вариант, чтобы добавить его и закрыть список. Сначала я собираюсь заняться переключением, потому что это проще всего.
Переключение
Иногда люди используют opacity или height, чтобы скрыть контент на экране, но это похоже на использование плаща-невидимки Гарри Поттера. Никто не может видеть, что там, но Гарри не перестает существовать, и вы все равно можете ткнуть его палочкой. В нашем случае пользователи программ чтения с экрана и клавиатуры могут по-прежнему получать доступ к невидимому списку.
Вместо того, чтобы сделать контент прозрачным или меньшим, я собираюсь использовать display: none, чтобы скрыть список. display: none удаляет контент из дерева специальных возможностей, чтобы к нему не мог получить доступ ни один пользователь, а не только те, кто его не видит. У меня всегда есть пара служебных классов для сокрытия вещей:
.hidden-all { display: none; } .hidden-visually { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; -webkit-clip-path: inset(50%); clip-path: inset(50%); border: 0; }
Так что теперь я могу просто переключать класс CSS .hidden-all в списке, когда захочу.
Просмотр вариантов
Открытие списка хорошо работает для пользователей мыши и сенсорных устройств. Наши стили создают приятную большую цель касания, и пользователи мыши могут кликать там, где им нравится.
Мы должны также позаботиться о пользователях клавиатуры. Некоторые из зрячих пользователей будут полагаться на клавиатуру, если у них возникнут проблемы с подвижностью или ловкостью. Обычно пользователи программы чтения с экрана находятся в режиме просмотра, который позволяет им нажимать клавиши со стрелками для навигации по контенту. Однако пользовательские элементы select обычно находятся внутри элементов формы, которые переводят программное обеспечение для чтения с экрана в режим форм. В режиме форм программное обеспечение для чтения с экрана может получать доступ к выделенным фокусом элементам, только когда пользователь нажимает клавишу Tab, если только мы не предоставим альтернативу. Наши элементы списка по умолчанию не фокусируемы, поэтому давайте поработаем над этой альтернативой.
Для этого я добавляю tabindex -1 к каждому элементу списка. Таким образом, я могу задать им выделение фокусом с помощью JavaScript, но они не будут частью обычного пути выделения фокусом клавиатуры на странице.
csOptions.forEach(function(option) { option.setAttribute('tabindex, '-1') })
Теперь я могу перемещать фокус с помощью клавиш со стрелками , а также с помощью мыши или касания экрана. Свойство документа activeElement является способом поиска, где в данный момент находится фокус клавиатуры. Я могу использовать это для циклического перемещения по элементам в списке и перемещения точки фокусировки вперед или назад, в зависимости от того, какая клавиша нажата.
function doKeyAction(whichKey) { const focusPoint = document.activeElement switch(whichKey) { case: 'ArrowDown': toggleList('Open') moveFocus(focusPoint, 'forward') break case: 'ArrowUp': toggleList('Open') moveFocus(focusPoint, 'back') break } }
Выбор
Клавиша Enter является традиционным вариантом для активации элемента, и мы хотим, чтобы это соответствовало нативному элементу выбора. Добавим еще один фрагмент…
case 'Enter': makeChoice(focusPoint) toggleList('Shut') setState('closed') break
… затем создайте функцию, которая захватывает текущий выделенный фокусом элемент и помещает его в текстовый элемент. Затем мы можем закрыть список и переместить фокус вверх.
function makeChoice(whichOption) { const optionText = whichOption.documentQuerySelector('strong') csInput.value = optionText }
Фильтрация
Стандартные элементы select имеют сочетания клавиш — при вводе буквы фокусом будет выделен первый вариант, который начинается с этой буквы. Если вы наберете букву еще раз, фокус переместится на следующий вариант, начинающуюся с этой буквы.
Это полезно, но здесь нет подсказки, чтобы сообщить пользователям, сколько вариантов может быть в этой категории, поэтому они должны экспериментировать, чтобы выяснить это. Мы можем улучшить опыт пользователей, отфильтровав только набор вариантов, соответствующий этой букве или последовательности букв. Затем зрячие пользователи смогут точно узнать, сколько вариантов у них есть, и продолжить фильтрацию, вводя другие буквы, если захотят. (Пользователи программы чтения с экрана не могут видеть оставшиеся варианты, когда набирают текст, но не волнуйтесь — у нас будет решение для них в следующем разделе).
Я собираюсь использовать метод .filter для создания нового массива, который содержит только те элементы, которые соответствуют текстовому значению ввода. Есть разные способы сделать это — моя цель состояла в том, чтобы избежать использования регулярных выражений, но вам стоит выбрать любой метод, который лучше всего подходит для вашего контента.
function doFilter() { const terms = csInput.value const aFilteredOptions = aOptions.filter(option => { if (option.innerText.toUpperCase().startsWith(terms.toUpperCase())) { return true } }) // hide all options csOptions.forEach(option => option.style.display = "none") // re-show the options which match our terms aFilteredOptions.forEach(function(option) { option.style.display = "" }) }
Отлично! Это выглядит и ведет себя очень хорошо. У нас есть еще одна проблема — для пользователя программы чтения с экрана это беспорядочная информация. Что сообщается API доступа браузера, так это то, что есть ввод, за которым следует какой-то кликабельный текст. Они связаны? Неизвестно! Что произойдет, если мы начнем вводить текст или нажмем один из кликабельных вариантов? Это загадка, когда ты не видишь, что происходит. Но мы можем это исправить.
ARIA
Атрибуты ARIA не предоставляют много дополнительных возможностей. Добавление атрибута aria-expanded=’true’ на самом деле ничего не расширяет. Что ARIA делает, так это предоставляет информацию о том, что происходит с API доступности, который затем может передать ее любой вспомогательной технологии, которая запрашивает это.
Требования WCAG говорят нам, что когда мы создаем пользовательские элементы, нам нужно убедиться, что в целом виджет сообщает нам свое имя, свою роль и текущее значение. И Chrome, и Firefox раскрывают дерево специальных возможностей в инструментах разработчика, поэтому вы можете проверить, как будет отображаться любой из ваших виджетов.
У нас уже есть имя для элемента ввода — оно происходит от метки, которую мы связали с элементом в самом начале. Нам не нужно называть любую другую часть поля, так как тогда кажется, что присутствует более одного элемента ввода. Нам также не нужно добавлять значение, потому что когда мы выбираем элемент из списка, он добавляется к текстовому вводу и, следовательно, предоставляется API.
Но программы чтения с экрана объявят этот пользовательский виджет выбора в качестве поля ввода текста, с некоторыми изображениями и списком рядом с ним.
На сайте ARIA Authoring Practices есть шаблон для комбинированных списков с прикрепленными списками. Он предоставляет все, что нужно, чтобы программа для чтения с экрана дала полезное описание нашего пользовательского виджета.
Я собираюсь добавить весь этот ARIA через JavaScript, вместо того, чтобы поместить его в HTML. Если JavaScript по какой-либо причине не работает, элемент может быть простым текстовым полем, и мы не хотим, чтобы программы чтения с экрана объявляли его более привлекательно.
csSelector.setAttribute('role', 'combobox') csSelector.setAttribute('aria-haspopup', 'listbox') csSelector.setAttribute('aria-owns', '#list') csInput.setAttribute('aria-autocomplete', 'both') csInput.setAttribute('aria-controls', 'list')
Следующее, что нужно сделать, это сообщить слепым пользователям, открыт ли или закрыт список. Для этой задачи я собираюсь добавить в группу атрибут aria-expanded и обновлять его значениями false или true каждый раз, когда список меняет состояние в функции переключения.
Последний штрих — добавить в виджет секретное сообщение о статусе. Мы можем использовать его для обновления количества доступных вариантов после того, как мы отфильтровали их, введя данные в поле ввода. Когда есть много вариантов для выбора, это помогает людям, которые не видят список, сокращая его и давая знать, на правильном ли они пути или нет. Для этого нам нужно сначала указать сообщение статуса в HTML.
<div id='custom-select-status' class='hidden-visually' aria-live='polite'></div>
Я использую стиль visually-hidden, чтобы его могли найти только пользователи программы чтения с экрана. Я использую aria-live, так что он будет объявляться тогда же, когда обновляться, а не только тогда, когда пользователь программы чтения с экрана проходит его. Активные области должны присутствовать при загрузке страницы, но нам нечего сказать о пользовательском элементе выбора, поэтому мы можем пока оставить его пустым.
Затем мы добавляем одну строку в функцию фильтрации, чтобы найти длину текущего списка.
updateStatus(aFilteredOptions.length)
Затем мы отправляем это в функцию, которая будет обновлять текущую область.
function updateStatus(howMany) { console.log('updating status') csStatus.textContent = howMany + " options available." }
Заключение
Давайте рассмотрим, что мы сделали, чтобы создать отличный пользовательский элемент выбора:
Использовали семантический HTML, чтобы его было легко интерпретировать с помощью вспомогательных технологий при расширении типов контента, которые мы можем включить в него
Добавили стили CSS, которые достаточно стабильны, чтобы нормально работать в различных визуальных средах, а также соответствуют нашим потребностям в брендинге
Использовали JavaScript, чтобы обеспечить базовый функционал нативного элемента
Добавили дополнительный JavaScript для получения полезного функционала, которого не хватает оригинальному элементу
Добавили атрибуты ARIA, чтобы обеспечить, чтобы цель и результаты использования элемента были доступны вспомогательным технологиям и обновлялись по мере взаимодействия пользователя с ним.
Вы можете посмотреть мой пользовательский шаблон элемента выбора на GitHub — я буду вносить дополнения по мере тестирования его на других вспомогательных технологиях, и буду рада предложениям по улучшению.
Шаблон ARIA, описанный выше, имеет множество примеров и настроек. Я надеюсь, что пошаговое выполнение этого примера покажет вам, для чего существует каждое из требований и как вы можете сделать так, чтобы они соответствовали вашим потребностям.
Автор: Julie Grundy
Источник: https://24ways.org
Редакция: Команда webformyself.