Статьи

Как сделать веб-компоненты доступными — краткое руководство

Эта статья была рецензирована Мэллори ван Ачтерберг. Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

В предыдущей статье я продемонстрировал, как создать многоэлементный веб-компонент. В разделе комментариев этой статьи читатели затронули очень важную тему доступности веб-компонентов. Несомненно, для сегодняшней сети жизненно важно быть доступным, поэтому давайте поговорим о том, что такое доступность, и посмотрим (на примере реального мира), как сделать веб-компонент более доступным.

Код в этой статье будет основан на коде из моей предыдущей статьи. Вы можете получить его копию из нашего репозитория GitHub или посмотреть демонстрацию доступного компонента в конце статьи.

Что означает доступность веб-компонента?

Говоря о доступности компонента, мы обычно учитываем следующие аспекты:

  1. Семантика разметки
  2. Поддержка клавиатуры
  3. Визуальная доступность

Давайте обсудим каждый аспект немного подробнее.

Семантика разметки

Я уверен, что вы слышали о программах чтения с экрана. Программа для чтения с экрана — это вспомогательное программное обеспечение, которое позволяет слепым или слабовидящим людям использовать приложения, читая вслух информацию, отображаемую на экране. Есть много программ для чтения с экрана, среди них NVDA и JAWS для Windows, ChromeVox для Chrome и VoiceOver для OS X.

Когда элемент получает фокус, программа чтения с экрана предлагает информацию об этом пользователю. Таким образом, когда HTML <input type="text"> сфокусирован, пользователь знает из программы чтения с экрана, что он имеет дело с текстовым полем (и может что-то вводить). Но если элемент просто <div> , программа чтения с экрана ничего не скажет об этом.

Чтобы решить эту проблему, мы можем использовать WAI-ARIA (Инициатива веб-доступности — Accessible Rich Internet Applications), чтобы добавить специальные атрибуты ARIA для расширения семантики разметки компонента. Эта дополнительная семантика помогает вспомогательным технологиям идентифицировать свойства, отношения и состояния в ваших пользовательских интерфейсах. Практические рекомендации по использованию ARIA можно найти здесь: WAI-ARIA Authoring Practices , в качестве альтернативы (для быстрого повышения квалификации) вы можете прочитать наше Введение в WAI-ARIA .

Поддержка клавиатуры

Цель состоит в том, чтобы сделать возможным взаимодействие с компонентом, используя только клавиатуру. WAI-ARIA определяет поведение и взаимодействие с клавиатурой для многих элементов управления пользовательского интерфейса . Чтобы узнать, какие ключи должны поддерживаться каким компонентом, найдите описание вашего компонента или подобное в спецификации и используйте его. Например, мультиселект похож на комбинированный список .

Даже при наличии доступа к клавиатуре рекомендуется информировать пользователей о том, какие комбинации клавиш / клавиш использовать для взаимодействия с компонентом (например, путем предоставления некоторых инструкций в приложении), поскольку это может быть неочевидным.

Визуальная доступность

Здесь речь идет об аспектах доступности, связанных с внешним видом компонента. Убедитесь, что вы можете ответить «да» на следующие вопросы:

  • Достаточно ли велики элементы и текст, чтобы их было ясно видно?
  • Ваш компонент выглядит так, как ожидается в режиме высокой контрастности?
  • Возможно ли использовать ваш компонент без цветов?

Помните, что не все слабовидящие пользователи юридически слепы . Есть много пользователей, которые (например) имеют слабое зрение или дальтонизм.

Обеспечение доступности веб-компонента Multiselect

Теперь мы собираемся сделать мультиселектор более доступным, используя все методы, описанные выше. В частности, мы собираемся:

  • расширить семантику разметки
  • добавить поддержку клавиатуры
  • подтвердить его визуальную доступность

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

Все фрагменты кода можно найти в файле multiselect.html .

Расширение семантики разметки

Основное правило доступности — использовать собственные элементы HTML поверх пользовательских. Это означает, что если вы можете использовать собственный элемент управления HTML со встроенной доступностью, сделайте это. Добавляйте атрибуты ARIA, только если вам действительно нужно создать пользовательский компонент. Если вы хотите узнать больше об этом, прочитайте « Избегание избыточности с помощью WAI-ARIA на страницах HTML» .

В нашем случае множественный выбор — это пользовательский компонент, поэтому нам нужно добавить атрибуты ARIA. Во-первых, давайте найдем компонент, похожий на множественный выбор в спецификации ARIA . После небольшого исследования кажется, что комбинированный список выглядит и ведет себя аналогично. Отлично, теперь давайте посмотрим, какие атрибуты ARIA нам нужно добавить в соответствии с описанием в выпадающем списке .

Из руководящих принципов мы видим, что нам нужно добавить следующие роли:

  1. role="combobox" для корневого элемента компонента
  2. role="listbox" к списку элементов во всплывающем окне
  3. role="option" для каждого элемента раскрывающегося списка

Атрибуты состояния арии, которые будут добавлены:

  1. aria-expanded="true/false" для корневого элемента, чтобы указать, открыт компонент или закрыт
  2. aria-selected="true/false" для каждого элемента раскрывающегося списка, чтобы указать выбранное состояние

combobox и listbox могут быть добавлены непосредственно в разметку компонента:

 <div class="multiselect" role="combobox"> <div class="multiselect-field"></div> <div class="multiselect-popup"> <ul class="multiselect-list" role="listbox"> <content select="li"></content> </ul> </div> </div> 

Чтобы добавить параметр роли для каждого элемента списка, мы refreshItems элементы в методе refreshItems . Этот новый метод вызывается при визуализации компонента:

 multiselectPrototype.render = function() { this.attachHandlers(); this.refreshField(); this.refreshItems(); }; multiselectPrototype.refreshItems = function() { var itemElements = this.itemElements(); for(var i = 0; i < itemElements.length; i++) { var itemElement = itemElements[i]; // set role and aria-selected property of an item itemElement.setAttribute("role", "option"); itemElement.setAttribute("aria-selected", itemElement.hasAttribute("selected")); } }; multiselectPrototype.itemElements = function() { return this.querySelectorAll('li'); }; 

Атрибут aria-expanded togglePopup может быть добавлен к togglePopup управления в методе togglePopup который (как следует из названия) отвечает за отображение и скрытие всплывающего окна:

 multiselectPrototype.togglePopup = function(show) { this._isOpened = show; this._popup.style.display = show ? 'block' : 'none'; // set aria-expanded property this._control.setAttribute("aria-expanded", show); }; 

Мы также инициализируем aria-selected свойство элементов в зависимости от их selected атрибута. Свойство aria-selected должно быть сохранено, чтобы отражать текущее состояние выбранного элемента. Мы можем сделать это в selectItem и selectItem :

 multiselectPrototype.selectItem = function(item) { if(!item.hasAttribute('selected')) { // set aria-selected property of selected item item.setAttribute('aria-selected', true); item.setAttribute('selected', 'selected'); this.fireChangeEvent(); this.refreshField(); } this.close(); }; multiselectPrototype.unselectItem = function(item) { // set aria-selected property of unselected item item.setAttribute('aria-selected', false); item.removeAttribute('selected'); this.fireChangeEvent(); this.refreshField(); }; 

И это все, свойства ARIA были добавлены. Следующий шаг — поддержка клавиатуры.

Добавление поддержки клавиатуры

Давайте откроем спецификацию и посмотрим на раздел « Клавиатурные взаимодействия », чтобы увидеть, какие взаимодействия нам нужно поддерживать.

Вот базовый набор клавиш, чтобы можно было использовать мультиселект только с клавиатуры:

  1. Alt + Up/Down Arrow — открыть / закрыть мультиселектор
  2. Esc — закрыть мультиселект
  3. Up/Down Arrow — перемещаться по элементам
  4. Enter — выбрать элемент при открытии мультиселекции
  5. Backspace — отменить выбор последнего выбранного элемента

Делая это фокусируемым

Самый первый шаг к добавлению поддержки клавиатуры — сделать компонент фокусируемым. Для этого нам нужно установить атрибут tabindex , поведение которого отличается в зависимости от значения tabindex :

  • положительное целое число — определяет порядок элемента при фокусировке клавиатуры
  • 0 — порядок элемента в фокусировке клавиатуры определяется браузером
  • -1 — элемент не может быть достигнут с помощью навигации фокуса клавиатуры, но теперь он может получить фокус программно, используя JavaScript focus() .

В случае пользовательских компонентов tabindex должен быть либо -1 либо 0 , потому что мы не можем знать порядок элемента на целевой странице. Таким образом, мы устанавливаем tabindex для поля множественного выбора в 0 прямо в разметке:

 <div class="multiselect-field" tabindex="0" aria-readonly="true"></div> 

Следующим шагом является обработка события keydown на множественном выборе:

 multiselectPrototype.attachHandlers = function() { this._control.addEventListener('keydown', this.keyDownHandler.bind(this)); ... }; 

Метод keyDownHandler вызывает конкретный обработчик ключа в зависимости от event.which свойства event.which :

 multiselectPrototype.keyDownHandler = function(event) { switch(event.which) { case 8: // Backspace this.handleBackspaceKey(); break; case 13: // Enter this.handleEnterKey(); break; case 27: // Escape this.handleEscapeKey(); break; case 38: // Up Arrow event.altKey ? this.handleAltArrowUpKey() : this.handleArrowUpKey(); break; case 40: // Down Arrow event.altKey ? this.handleAltArrowDownKey() : this.handleArrowDownKey(); break; default: return; } // prevent native browser key handling event.preventDefault(); }; 

После того, как нажатие клавиши обработано, мы запрещаем браузеру выполнять стандартное действие, вызывая event.preventDefault() .

Открыть / Закрыть с клавиатуры

Комбинация клавиш Alt + Down Arrow должна открыть мультиселектор, а клавиши Alt + Up Arrow и Esc должны закрыть его:

 multiselectPrototype.handleAltArrowDownKey = function() { this.open(); }; multiselectPrototype.handleAltArrowUpKey = function() { this.close(); }; multiselectPrototype.handleEscapeKey = function() { this.close(); }; 

Методы open и close просто вызывают метод togglePopup :

 multiselectPrototype.open = function() { this.togglePopup(true); }; multiselectPrototype.close = function() { this.togglePopup(false); }; 

Во-первых, мы устанавливаем tabindex каждого элемента множественного tabindex в -1 , чтобы они стали фокусируемыми:

 multiselectPrototype.refreshItems = function() { var itemElements = this.itemElements(); for(var i = 0; i < itemElements.length; i++) { var itemElement = itemElements[i]; ... // set item tabindex attribute itemElement.setAttribute("tabindex", -1); } // initialize focused item index this._focusedItemIndex = 0; }; 

Свойство _focusedItemIndex хранит индекс _focusedItemIndex элемента.

Up Arrow Down Arrow позволяют пользователю перемещаться по элементам в списке, поддерживая текущий индекс выделенного элемента:

 multiselectPrototype.handleArrowDownKey = function() { this._focusedItemIndex = (this._focusedItemIndex < this.itemElements().length - 1) ? this._focusedItemIndex + 1 // go to the next item : 0; // go to the first item this.refreshFocusedItem(); }; 

Если Down Arrow нажата в конце списка, фокус переходит к первому элементу.

Если Up Arrow нажата на первом элементе списка, фокус переходит к последнему элементу списка:

 multiselectPrototype.handleArrowUpKey = function() { this._focusedItemIndex = (this._focusedItemIndex > 0) ? this._focusedItemIndex - 1 // go to the previous item : this.itemElements().length - 1; // go to the last item this.refreshFocusedItem(); }; 

Метод refreshFocusedItem устанавливает фокус на элемент с индексом, равным _focusedItemIndex :

 multiselectPrototype.refreshFocusedItem = function() { this.itemElements()[this._focusedItemIndex].focus(); }; 

Наконец, нам нужно изменить методы open и close таким образом, чтобы при открытии мультиселекции фокус переходит к элементу с индексом _focusedItemIndex , а когда закрывается элемент управления, фокус возвращается к полю множественного выбора:

 multiselectPrototype.open = function() { this.togglePopup(true); this.refreshFocusedItem(); }; multiselectPrototype.close = function() { this.togglePopup(false); this._field.focus(); }; 

Теперь мы можем добавить немного CSS, чтобы сфокусированный элемент выглядел более привлекательно:

 ::content li:focus { outline: dotted 1px #333; background: #efefef; } 

Выбрать / отменить выбор элемента с помощью клавиатуры

Клавиша Enter позволяет пользователю выбрать текущий фокусированный элемент. Если мультиселект открыт, мы получаем сфокусированный элемент и выбираем его методом selectItem :

 multiselectPrototype.handleEnterKey = function() { if(this._isOpened) { var focusedItem = this.itemElements()[this._focusedItemIndex]; this.selectItem(focusedItem); } }; 

Клавиша Esc должна удалить последний выбранный элемент. Если какие-либо выбранные элементы присутствуют, мы берем последний и отменяем его выбор, вызывая метод unselectItem :

 multiselectPrototype.handleBackspaceKey = function() { var selectedItemElements = this.querySelectorAll("li[selected]"); if(selectedItemElements.length) { this.unselectItem(selectedItemElements[selectedItemElements.length - 1]); } }; 

Теперь мы поддержали все необходимые взаимодействия с клавиатурой, поэтому компонент можно использовать только с клавиатурой.

Визуальная доступность

Размер компонента

Multiselect имеет относительные размеры в em и его размер зависит от размера шрифта контейнера. Таким образом, он масштабируется и может быть легко увеличен при необходимости:

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

Режим высокой контрастности

Люди с плохим зрением или другими нарушениями зрения иногда используют высококонтрастный режим. OS X позволяет пользователям включать высокую контрастность в настройках, тогда как Windows предоставляет специальные темы высокой контрастности . Существует также популярное расширение Chrome, которое (на удивление) называется High Contrast и позволяет пользователям просматривать веб-страницы с помощью высококонтрастных цветовых фильтров

Давайте посмотрим, как выглядит наш компонент в режиме высокой контрастности:

мультиселект в режиме высокой контрастности

Выглядит нормально, но есть проблема: выбранные элементы трудно отличить от не выбранных. Небольшое изменение цвета для выбранных и сфокусированных элементов устраняет проблему:

мультиселект в режиме высокой контрастности - исправлено

Без цветов

Доступность цвета является еще одним важным аспектом визуальной доступности. Есть много дальтоников, исследующих сеть. Это означает, что цвет не должен быть единственным способом донести до пользователя важную информацию. Здесь мы можем проверить, как выглядит наш компонент в режиме градаций серого (обычно это обеспечивается ОС или специальными приложениями). Сказав это, наш компонент состоит только из серых цветов, так что это проверка, которую мы можем пропустить.

демонстрация

Реализовав все вышеперечисленное, это конечный продукт:

Вывод

Чтобы сделать веб-компонент более доступным, мы должны быть уверены, что:

  • Разметка семантическая, поэтому вспомогательные технологии, такие как программы чтения с экрана, могут помочь пользователям при взаимодействии с компонентом. Для этого попробуйте использовать собственные элементы управления HTML или, в случае пользовательских элементов управления, добавить значимые атрибуты ARIA.
  • Компонент может использоваться только с клавиатуры. Чтобы добиться этого, сделайте ваш компонент фокусируемым с tabindex . Реализуйте взаимодействие с клавиатурой в соответствии с практическими рекомендациями ARIA.
  • Компонент может использоваться в режиме высокой контрастности и без цветов.

Спасибо за прочтение. Не стесняйтесь делиться своим опытом в создании веб-компонентов, доступных в комментариях.