Как сделать изменение контента доступным.
Если у вас есть динамически изменяющаяся часть страницы и вы задумались о её доступности, то может возникнуть закономерный вопрос о том, как же это сделать. Это могут быть:
- Чаты;
- Прогресс-бары и таймеры;
- Виджеты с новостями и погодой;
- Разные ошибки и оповещения: новое сообщение, лайк, подписка;
- Тикеры (биржевая информация о котировках акций, индексов, облигаций), курсы валют;
- Спортивная статистика и многое другое.
Раньше вспомогательные технологии (в том числе скринридеры) не умели правильно их обрабатывать. Пользователи не могли узнать о том, появилась ли какая-то ошибка или новые данные до тех пор, пока не возвращались в предыдущий блок или не доходили до конца страницы. Теперь проблему доступности динамически изменяющегося контента страниц можно решить с помощью ARIA.
Если вы не знакомы с этой аббревиатурой, то WAI-ARIA (англ. Web Accessibility Initiative — Accessible Rich Internet Applications) или просто ARIA — это стандарт, состоящий из набора специальных ролей и атрибутов, которые добавляются в разметку и расширяют или дополняют функции стандартных HTML-элементов.
Всё, что нам нужно — это сделать часть страницы, в которой происходят изменения. В терминологии ARIA, это называется «live region» или «интерактивной областью». В стандарте можно найти такое определение:
Live Region — это воспринимаемые области страниц, которые обычно обновляются в результате внешнего события, когда пользователь сделал фокус где-то в другом месте. Эти области не всегда обновляются из-за взаимодействия пользователей с ними. Такая практика стала обычным делом в результате активного использования Ajax.
Таким образом, главная цель таких областей — сообщать скринридерам как правильно обрабатывать изменения контента, которые не обязательно зависят от пользователей.
Чтобы сделать интерактивную область на странице, нам нужно просто добавить к любому родительскому элементу ARIA-атрибут aria-live="" или специальную ARIA-роль. Тогда изменения всех его дочерних элементов станут доступными для скринридеров. Теперь они будут знать, как обрабатывать обновления содержимого таких элементов.
В ARIA есть несколько таких ролей и атрибутов. Давайте сначала поговорим о ролях.
Роли интерактивных областей Скопировать ссылку
ARIA-ролей, которые делают часть страницы интерактивной областью, не так много. Используются они вот так: role="alert" Вот их полный список:
alert;status;log;timer;marquee.
Разберёмся с каждой ролью по порядку.
Alert Скопировать ссылку
Тип интерактивной области, который содержит важную в определённый момент времени информацию. Это может быть сообщение об ошибке, предупреждение, которое появляется на экране после каких-то действий пользователя или без его участия (внезапная ошибка на стороне сервера). Такое сообщение может быть как текстовым, так и звуковым.
Рассмотрим простой пример, в котором мы предупреждаем пользователей о чём-то действительно важном:
<div class="warning" role="alert">
Вы слишком долго смотрели в бездну,
и теперь бездна смотрит в вас.
</div>
Скринридер моментально объявит его в момент появления и прервёт другое объявление, если оно было.
👉 Для максимальной совместимости используйте role="alert" вместе с атрибутом aria-live="assertive". При этом такой элемент всё ещё может быть неправильно объявлен VoiceOver на iOS. Внесём небольшие изменения в наш пример:
<div class="warning" role="alert" aria-live="assertive">
Вы слишком долго смотрели в бездну,
и теперь бездна смотрит в вас.
</div>
Получается, что мы продублировали уже встроенное в role="alert" поведение с помощью значения assertive атрибута aria-live. Последнее как раз подсказывает скринридерам, что объявить об изменениях нужно немедленно.
Status Скопировать ссылку
Область с такой ролью содержит дополнительную информацию, которая не особо важна и описывает состояние изменений (status bar). Это может быть информация о том, что действие пользователя успешно или наоборот, что требуется подождать завершения какого-то процесса или где-то есть ошибка. Например, такую роль можно задать сообщению об успешном автосохранении текста или использовать при валидации полей в форме регистрации.
Кстати, в скринридерах есть специальная команда, которая помогает узнать пользователям о статусе. В NVDA она вызывается сочетанием клавиш Ins End, а в JAWS — Ins 3.
В примере мы сообщаем пользователям о том, что изменения сохранены:
<div class="status-message" role="status">
Мы сохранили ваши изменения автоматически,
не благодарите.
</div>
Скринридер объявит это с паузой, а не сразу же, как в случае с role="alert".
👉 В role="status" встроено поведение атрибута aria-live="polite". Для максимальной совместимости их рекомендуется использовать вместе. Поэтому пример выше выглядит теперь так:
<div class="status-message" role="status" aria-live="polite">
Мы сохранили ваши изменения автоматически,
не благодарите.
</div>
Скринридер сообщит об успешном автосохранении с паузой и не будет прерывать другие объявления.
Log Скопировать ссылку
Тип интерактивной области, в которой содержатся логи. Например, история сообщений из чатов, список ошибок и тому подобное. Для логов имеет важное значение последовательность, в которой появляется новая информация. Вспомните журнал событий в Windows.
В этом примере показано обновление контента в чате. Когда пользователь вводит сообщение в текстовое поле, то оно добавляется в конец переписки.
<div role="log">
<h4>История сообщений</h4>
<ul>
<li>
Одолжишь своего вельш-корги-кардигана
до понедельника? Очень нужно.
</li>
</ul>
</div>
Теперь скринридер объявляет о новых комментариях после того, как пользователь перестал набирать или отправлять сообщение.
👉 role="log" на всякий случай лучше сочетать с атрибутом aria-live="polite":
<div role="log" aria-live="polite">
<h4>История сообщений</h4>
<ul>
<li>
Одолжишь своего вельш-корги-кардигана
до понедельника? Очень нужно.
</li>
<li>Тебя снова взломали?</li>
</ul>
</div>
В этом случае все изменения будут наверняка объявляться с паузой и не прерывать другие более важные изменения.
Marquee Скопировать ссылку
В такой области содержится информация, которая быстро изменяется. Эта роль похожа на log, но в этом случае последовательность обновления информации не имеет значения. Простой пример, где может пригодиться role="marquee" — тикеры и курсы валют.
В этом примере мы добавляем role="marquee" для блока с информацией о курсах валют:
<ul role="marquee">
<li>¥ 9999,56 ₽ за юань</li>
<li>F 100000000000,32 ₽ за фронтендкоин</li>
</ul>
Скринридер будет объявлять об изменениях в этом блоке тогда, когда пользователь сделает фокус на нём. Курсы валют часто изменяются, поэтому постоянные объявления об этом будут только раздражать пользователей.
👉 role="marquee" стоит использовать вместе с атрибутом aria-live="off":
<ul role="marquee" aria-live="off">
<li>¥ 9999,56 ₽ за юань</li>
<li>F 100000000000,32 ₽ за фронтендкоин</li>
</ul>
Мы просто продублировали поведение role="marquee" по умолчанию для максимальной совместимости.
Timer Скопировать ссылку
Эта роль нужна для тех областей, в которых содержатся счётчики, отсчитывающие время в обычном и обратном порядке. Например, таймер обратного отсчёта, часы или секундомер.
<div role="timer">
<!-- Тут стремительно утекает время -->
</div>
В эту роль по умолчанию встроено поведение, при котором скринридер не будет объявлять об изменениях, произошедших с таймером, и пользователь узнает о них только при фокусе на нём.
👉 Элементу с role="timer" стоит также задать атрибут aria-live="off" для полной совместимости со всеми вспомогательными устройствами и браузерами:
<div role="timer" aria-live="off">
<!-- Тут стремительно утекает время -->
</div>
Если нужно, чтобы скринридер объявлял об изменениях через определённый интервал времени, то сделать это можно при помощи JavaScript. Нам нужно переключать aria-live="off" на aria-live="polite" через нужный промежуток времени, например, 60 минут.
Атрибуты интерактивных областей Скопировать ссылку
Теперь поговорим об атрибутах, которые делают любую область страницы интерактивной. Их всего четыре:
aria-live;aria-atomic;aria-relevant;aria-busy.
Рассмотрим каждый из них.
Aria-live Скопировать ссылку
Этот атрибут используется для определения важности изменений, которые произошли с элементами.
То есть значения данного атрибута отражают то, насколько срочно и быстро вспомогательным технологиям нужно сообщить пользователям об этих изменениях. У атрибута есть три значения: off, polite и assertive.
off(значение по умолчанию) — указывает на низший приоритет, поэтому такие изменения не объявляются. Это поведение встроено в элементы сrole="marquee"иrole="timer". Его можно задавать тем областям, которые не важны, или слишком быстро изменяются.polite— обозначает низкий уровень приоритета. Используется в случаях, когда в области происходят изменения, которые вспомогательным технологиям не нужно объявлять моментально. Скринридеры делают перед таким объявлением паузу, не прерывают текущие задачи и ждут, пока пользователь перестанет взаимодействовать с интерфейсом. Так себя ведут элементы сrole="status"иrole="log". Подходит для оповещений о новых сообщениях, лайках, автосохранении и тому подобном.assertive— указывает на наивысший уровень приоритета. О таких изменениях будет объявлено сразу же, а изменения с более низким приоритетом встанут в очередь и будут объявлены позже. Так себя ведут элементы сrole="alert", так что этот атрибут можно использовать для сообщений о важных изменениях, например, о серверной ошибке или о том, что данные не сохранились. Спецификация не рекомендует использовать это значение, когда нет необходимости сразу же сообщать пользователям об изменениях.
Приведу пару простых примеров использования aria-live="polite" и aria-live="assertive".
В этом примере с aria-live="polite" при нажатии на кнопку с помощью скрипта меняется название блюда в параграфе.
<p aria-live="polite">Моё любимое блюдо
<span id="food">лютефиск</span>.
</p>
<button type="button">Следующее блюдо</button>
Скринридер сделает перед объявлением паузу.
А здесь у нас есть форма с несколькими настройками и кнопкой сохранения. Если изменения не сохранились, то должно появляться сообщение об этом. Зададим ему aria-live="assertive":
<form>
<p>
<label for="devil-fruit">Любимый дьявольский фрукт</label>
<input type="text" id="devil-fruit">
</p>
…
<button type="submit">Сохранить настройки</button>
</form>
<div class="alert-window" role="alert" aria-live="assertive">
Ваши настройки не сохранились,
попробуйте ещё раз, йо-хо-хо!
</div>
Здесь скринридер сделает объявление немедленно.
❗ В стандарте WAI-ARIA также указано, что в некоторых случаях вспомогательные технологии могут переопределять значения атрибута aria-live и объявлять о каких-то изменениях моментально.
Aria-atomic Скопировать ссылку
Этот атрибут необязательный и влияет на то, в каком объёме вспомогательные технологии объявят об изменениях: это будет весь контент целиком или только его изменившаяся часть.
У атрибута есть всего два значения — false и true.
false(значение по умолчанию) — значение, при котором вспомогательные технологии сообщат только об изменениях.true— при этом значении объявляется весь контент, включая изменившуюся часть.
При выборе нужного значения для aria-atomic="" надо понять, важен ли для понимания изменений контекст. В большинстве случаев достаточно оставить значение по умолчанию.
В этом примере важно сохранить контекст, так что можно использовать aria-atomic="true":
<p aria-live="polite" aria-atomic="true">Моё любимое блюдо
<span id="food">лютефиск</span>.
</p>
<button type="button">Следующее блюдо</button>
Теперь скринридер будет зачитывать всё предложение целиком, а не только ту часть, которая изменяется после нажатия на кнопку.
Aria-relevant Скопировать ссылку
Цель этого атрибута — сообщить вспомогательным технологиям о том, какие именно изменения произошли на странице и, соответственно, в дереве доступности. Это может быть удаление старого или добавление нового контента. Атрибут необязательный.
aria-relevant="" может содержать одно или несколько значений, разделённых пробелом.
additions— на страницу добавлена новая информация.removals— информация удалена.text— добавлен новый текст или эквивалентная ему информация, например, новое содержимое атрибутаalt.additions text(значение по умолчанию) указывает на то, что текст был изменён и появилась новая информация на странице.all— все возможные значения. Эквивалентно значению"additions removals text".
На самом деле реальных сценариев использования этого атрибута мало. Он либо не работает во многих браузерах и скринридерах, либо его советуют вообще не использовать и прибегать к альтернативным методам.
Самый реалистичный сценарий его использования — это список друзей. Когда какой-то друг вышел из сети и больше не активен, то мы можем использовать этот атрибут, чтобы сообщить пользователю об этом. Нам нужно задать для списка aria-relevant="all". Тогда некоторые скринридеры объявят об удалении контакта. Это работает в JAWS, когда удаляется дочерний элемент (с родительским уже не работает). На VoiceOver и NVDA этот атрибут никак не влияет.
Aria-busy Скопировать ссылку
aria-busy="" даёт вспомогательным технологиям знать, обновляется ли сейчас содержимое элемента или нет. Атрибут имеет смысл применять тогда, когда на странице происходит автообновление контента. Что-то было удалено, что-то добавлено, какая-то его часть изменилась, и обо всё этом нам надо сообщить сразу за один раз. Это может быть полезно в случаях, когда на нашей странице есть спортивная статистика, которая обновляется в режиме реального времени, текстовый документ, который может редактировать несколько человек, какой-то новостной виджет или виджет с погодой.
У aria-busy="" есть два значения — false и true.
false(значение по умолчанию) — при этом значении вспомогательные технологии не ждут, пока изменения завершаться.true— это значение говорит вспомогательным технологиям, что им нужно подождать, пока элемент не закончит изменяться, после чего они могут собрать все изменения и сделать объявление. То есть во время обновления контента пользователи скринридеров, например, не смогут читать эту обновляющуюся часть.
В примере ниже у нас есть спортивные результаты, которые регулярно обновляются в ходе соревнований:
<h2>Текущий счёт</h2>
<p role="score" aria-live="polite" aria-busy="true">9:0</p>
Для того, чтобы вся информация объявлялась после всех изменений, сначала добавим атрибут aria-busy со значением true, а потом, с помощью JavaScript, заменим его значение на false или вообще его удалим, когда все изменения завершатся.
Краткие выводы Скопировать ссылку
Если у вас на странице есть часть, содержимое которой изменяется, то нужно сделать её интерактивной областью. Тогда скринридеры смогут держать своих пользователей в курсе всех изменений. Сделать такую часть интерактивной можно с помощью role="alert", role="status", role="log", role="marquee", role="timer" и атрибута aria-live.
Используйте role="alert" для важных ошибок, предупреждений. Для большей совместимости добавляйте её для нужных элементов вместе с атрибутом aria-live="assertive" и aria-atomic="true" (опционально).
role="status" подходит для сообщений о менее важных ошибках и предупреждениях. Например, сообщение об автосохранении, неправильно заполненном поле и тому подобное. Для совместимости следует сочетать эту роль с атрибутом aria-live="polite".
Если вам нужно сделать доступными историю сообщений, список ошибок и что-то, где важна последовательность обновления информации, используйте role="log". Для большей совместимости используйте вместе с ней атрибут aria-live="polite".
Когда у вас на странице есть тикеры, курсы валют или любой другой элемент, информация в котором быстро изменяется, то задайте для них role="marquee". Для совместимости дополните её aria-live="off".
Для таймера, счётчика или секундомера задавайте role="timer". Не забудьте о aria-live="off" для лучшей совместимости.
За то, насколько срочно нужно сообщить об изменениях, отвечает aria-live="". Там, где об изменениях не нужно объявлять, используйте aria-live="off". В большинстве случаев не нужно срочно сообщать об изменениях, поэтому в них пригодится aria-live="polite". Иногда, когда речь идёт о важном сообщении, например, серверной ошибке, можно использовать aria-live="assertive".
aria-atomic="" — необязательный атрибут. Он влияет на то, сообщит ли скринридер о контексте или объявит только об изменениях. По умолчанию всем элементам задан aria-atomic="false", то есть скринридеры сообщают только об изменениях контента. Если заменить его на aria-atomic="true", то скринридеры будут зачитывать всё целиком, включая неизменную часть. В большинстве случаев нет необходимости изменять поведение по умолчанию.
Ещё один необязательный атрибут — aria-relevant="". Он нужен для определения типа изменений контента. У него есть несколько значений, которые можно перечислять через пробел. Реальных сценариев использования мало (удаление или добавление друга в список друзей), а многие скринридеры его игнорируют.
Последний необязательный атрибут — aria-busy="". Сообщает вспомогательным технологиям обновляется ли сейчас содержимое элемента или нет. По умолчанию элементам задан aria-busy="false". Скринридеры объявляют об изменениях не дожидаясь, пока они завершатся. В некоторых случаях можно использовать aria-busy="true", когда нужно дождаться всех обновлений. Может быть полезно для спортивной статистики или текстового документа в режиме группового редактирования.
Что ещё можно почитать Скопировать ссылку
- Стандарт WAI-ARIA 1.2.
- Заметка Hiding and Updating Content на Web Fundamentals.
- Несколько примеров использования ARIA-атрибутов в ARIA Live Regions на MDN.
- ARIA Live Regions Ришабха Рао.
- Перевод статьи Как сделать сообщения об ошибках доступными Хидде де Вриса.




