Изменяющаяся или «живая» область (live region) — это часть страницы, где происходят изменения, о которых автоматически расскажут пользователям скринридеры и другие вспомогательные технологии.
Раньше скринридеры не умели рассказывать о динамически изменяющемся содержимом. Например, что при отправке формы возникла ошибка или об успешном сохранении изменений. Пользователям приходилось возвращаться в начало и конец блока или целой страницы. Как вы догадались, обычно об изменениях узнавали случайно.
Главная цель изменяющейся области — указать вспомогательным технологиям как правильно обрабатывать изменения содержимого страницы. Выгода для пользователей — они точно узнают обо всех изменениях без лишних нажатий на клавиши.
Когда и как использовать
СкопированоИзменяющиеся области охватывают любое содержимое на странице, которое добавляется, удаляется или загружается. Например:
- ошибки к полям и целым формам;
- сообщения об успешности действий пользователя;
- оповещения;
- прогресс загрузки или удаления;
- логи, чаты с сообщениями, списки друзей, фид;
- таймеры;
- биржевые тикеры, бегущие строки, прогнозы погоды, карусели.
Основные правила использования изменяющихся областей:
- В области находится только изменяющееся содержимое. Не делайте «живой» всю страницу.
- Не делайте изменяющимися областями все части страницы с изменениями. Например, об ошибке в форме рассказать важно, а об изменении названия кнопки не всегда.
- Следите за встроенными в теги свойствами и узнавайте, что делают другие ARIA-атрибуты. Например, достаточно задать кнопке, которая раскрывает меню, состояние
aria.- expanded - Изменяющимися областями могут быть интерактивные элементы и простой текст. Исключение —
arialи роль- live = "assertive" alert. Их добавляют только для текста. - Хорошо связывать элемент, который приводит к изменениям на странице, и изменяющуюся область. Используйте
ariaили- describedby aria.- labelledby
Чтобы изменяющаяся область заработала, в ней должно что-то поменяться. Если точнее, должен измениться DOM (Document Object Model) и дерево доступности (Accessibility Tree). Есть несколько способов это сделать. Спойлер: во всех случаях нужен JavaScript 🪄
Первый способ — удалить или добавить целый элемент в DOM. Это можно сделать с помощью JavaScript-методов манипуляции с элементами из DOM — .create, .inner и так далее. Подробнее в статье «Element».
Также можно менять значения display с none на block, visibility с hidden на visible и удалять и добавлять HTML-атрибут hidden.
Второй способ — удалить или добавить только содержимое элемента, при этом сам элемент всегда есть на странице. Тут снова поможет JavaScript, особенно метод .text.
Другой путь — добавлять новое содержимое по мере загрузки страницы или после перезагрузки.
Наконец, можно добавить ARIA-атрибут или роль интерактивной области к элементу, когда это нужно, и затем удалить. Так часто поступают с таймерами.
Как добавить
СкопированоЕсть несколько способов.
- Использовать теги, в которые уже встроены нужные роли и свойства, —
<output>и<progress>. - Задействовать атрибуты
ariaи опционально- live aria,- busy ariaили- atomic aria.- relevant - Добавить к тегам специальные ARIA-роли
status,log,marquee,timerиalert. - ARIA Notify.
Обычно проблему доступности динамически изменяющегося контента решают с помощью WAI-ARIA или просто ARIA (Web Accessibility Initiative — Accessible Rich Internet Applications).
HTML-теги
СкопированоПервый полезный тег — <output>. Его используют для вывода результатов. Это могут быть математические расчёты как в калькуляторе или правильное пропущенное слово в предложении в тестах на знание грамматики. Ещё один неочевидный случай использования — всплывающие уведомления или тосты. Это оповещения, которые выезжают внизу или вверху экрана и исчезают через какое-то время.
Внутри тега лучше размещать фразовый контент. Например, заголовки, списки или параграфы. Если вложите внутрь кнопку, ссылку или таблицу, скринридеры могут вас не понять 🤔
Содержимое <output> автоматические зачитывается скринридерами благодаря встроенной в тег роли status. Это одна из ролей «живых» областей.
У status невысокий приоритет объявления. Это значит, что скринридер расскажет об изменениях внутри <output> после другой, более приоритетной информации. Например, после сообщения о том, что нажата кнопка, или после текста ошибки с ролью alert.
К сожалению, пока <output> поддерживается не во всех браузерах и скринридерах. На всякий случай задавайте тегу явную роль status.
<output role="status"> <!-- Содержимое тега --></output>
<output role="status">
<!-- Содержимое тега -->
</output>
Пример всплывающего уведомления с <output> 🧇 Обратите внимание, что кнопку для закрытия уведомления добавляем рядом с изменяющейся областью, а не внутри.
<button>Сохранить</button><div> <output role="status"></output> <button aria-label="Закрыть">X</button></div>
<button>Сохранить</button>
<div>
<output role="status"></output>
<button aria-label="Закрыть">X</button>
</div>
Второй тег — <progress>. Его используют для индикаторов выполнения задач — прогресс-баров. Это может быть индикатор загрузки файла или очистки папки с удалёнными письмами.
В тег по умолчанию встроена роль progressbar. Это не роль изменяющейся области, но скринридеры всё равно объявляют о прогрессе загрузки в процентах или при помощи особого звукового оповещения.
Не забывайте задавать <progress> атрибуты max и value. Без них скринридеры не смогут отследить прогресс загрузки, когда у неё есть чёткие начало и конец. Так что изменяйте значение value с помощью JavaScript и явно задавайте значение max.
Если вложите внутрь <progress> кнопку, ссылку, заголовок и другие элементы, для вспомогательных технологий это просто текст без встроенных в теги ролей.
Ещё у <progress> должна быть подпись, чтобы пользователи понимали, что загружается. Для видимой подписи используйте <label>, а для доступной только вспомогательным технологиям — aria.
<!-- Подпись с <label> --><label for="progress-bar">Очищаем корзину</label><progress id="progress-bar" max="100" value="0"></progress><label> Очищаем корзину <progress max="100" value="0"></progress></label><!-- Подпись с aria-label --><progress aria-label="Очищаем корзину" max="100" value="0"></progress>
<!-- Подпись с <label> -->
<label for="progress-bar">Очищаем корзину</label>
<progress id="progress-bar" max="100" value="0"></progress>
<label>
Очищаем корзину <progress max="100" value="0"></progress>
</label>
<!-- Подпись с aria-label -->
<progress aria-label="Очищаем корзину" max="100" value="0"></progress>
Посмотрим на прогресс-бар на практике. Для подписи используем <label>, с помощью JavaScript обновим значение атрибута value и визуальный индикатор в виде числа и заливки.
<label for="progress-bar">Очищаем корзину</label><div>0%</div><progress id="progress-bar" value="0" max="100"></progress><button>Очистить</button>
<label for="progress-bar">Очищаем корзину</label>
<div>0%</div>
<progress id="progress-bar" value="0" max="100"></progress>
<button>Очистить</button>
Бывают ситуации, когда <progress> связан с другой частью страницы с изменениями. Например, подгружается новое содержимое. При этом неизвестно, где у него начало и конец. В этом случае добавьте к нужной части страницы aria и свяжите её с прогресс-баром с помощью aria.
<div role="feed" aria-busy="true" aria-describedby="progress-bar"> <!-- Содержимое, которое сейчас обновляется --></div><progress id="progress-bar" aria-label="Обновление фида"></progress>
<div role="feed" aria-busy="true" aria-describedby="progress-bar">
<!-- Содержимое, которое сейчас обновляется -->
</div>
<progress id="progress-bar" aria-label="Обновление фида"></progress>
ARIA-атрибуты
СкопированоДругой способ сделать часть страницы изменяющейся областью — специальные ARIA-атрибуты.
Чаще всего достаточно задать элементу aria со значением polite. Такие объявления не будут врываться без спроса и прерывать действия пользователей. Хорошие случаи использования — сообщения о сохранении данных, успешной подписке на рассылку, отправки письма и похожие ситуации.
<form> <!-- Поля, чекбоксы и всё, что душе угодно --> <button type="submit" aria-describedby="success">Отправить</button> <span id="success" aria-live="polite"> Мы получили вашу заявку. На обработку уйдёт 3 рабочих дня. </span></form>
<form>
<!-- Поля, чекбоксы и всё, что душе угодно -->
<button type="submit" aria-describedby="success">Отправить</button>
<span id="success" aria-live="polite">
Мы получили вашу заявку. На обработку уйдёт
3 рабочих дня.
</span>
</form>
Другое значение aria — assertive. Используйте его для срочных оповещений, о которых надо рассказать прямо сейчас. Так что применяйте aria с осторожностью. Значение подойдёт для критически важных ошибок, которые прерывают действия пользователей. Например, когда пропало интернет-соединение и пользователь из-за этого не может отправить заявку.
<form> <!-- Поля, чекбоксы и всё, что душе угодно --> <button type="submit" aria-describedby="fail">Отправить</button> <span id="fail" aria-live="assertive" aria-atomic="true"> У нас проблемы с сервером, так что заявка не отправилась. Нам нужно 5 часов, чтобы всё починить. </span></form>
<form>
<!-- Поля, чекбоксы и всё, что душе угодно -->
<button type="submit" aria-describedby="fail">Отправить</button>
<span id="fail" aria-live="assertive" aria-atomic="true">
У нас проблемы с сервером, так что заявка не отправилась.
Нам нужно 5 часов, чтобы всё починить.
</span>
</form>
Последнее значение aria — off. Такие области всё ещё изменяющиеся, но пользователи узнают об изменениях в них только если сделали фокус на них или элементах внутри.
Остальные ARIA-атрибуты изменяющихся областей необязательные и нужны в особых ситуациях.
aria помогает скринридерам понять, что элемент сейчас изменяется и нужно пока ни о чём не рассказывать и подождать окончания этих изменений.
Атрибут пригодится для сложных компонентов. Например, для фида с ролью feed или ленты новостей как в социальных сетях. В этом случае можно задать на время загрузки aria всей области с новостями, а после удалить. Так скринридеры расскажут об изменениях только тогда, когда пользователь нажмёт кнопку «Обновить ленту» и все элементы загрузятся.
<div role="feed" aria-busy="true"> <!-- Тут загружаются посты --></div>
<div role="feed" aria-busy="true">
<!-- Тут загружаются посты -->
</div>
aria рассказывает скринридеру об объёме изменений, о которых надо рассказать. Это вся изменяющаяся область или её часть. Атрибут крайне полезен для таймеров.
Если используете aria в таймере, вспомогательные технологии будут правильно объявлять текущее время: сначала часы, даже если они не изменились, потом минуты. Без атрибута скринридеры расскажут только об изменившихся минутах.
<div role="timer" aria-live="polite" aria-atomic="true"> <span id="timer-hours"></span> <span id="timer-mins"></span></div>
<div role="timer" aria-live="polite" aria-atomic="true">
<span id="timer-hours"></span>
<span id="timer-mins"></span>
</div>
aria сообщает скринридерам, о каком типе изменений рассказать. Это может быть добавление текста или элементов, их удаление или всё вместе. Атрибут особенно полезен в случае чата в мессенджере или обновления списка друзей.
Для добавляющихся элементов используйте значение additions, для добавляющегося текста — text, для удаляющегося содержимого — removals. Когда в изменяющейся области важны все типы изменений, используйте all.
Если нужно рассказать пользователям об удалении и добавлении содержимого, можно объединить сразу два значения. В этом примере со списком друзей используем aria.
<ul id="friend-list" aria-live="polite" aria-relevant="additions removals"> <!-- Ссылки на аккаунты друзей --></ul>
<ul
id="friend-list"
aria-live="polite"
aria-relevant="additions removals"
>
<!-- Ссылки на аккаунты друзей -->
</ul>
ARIA-роли
СкопированоПока HTML не покрывает все случаи, когда нужна изменяющаяся область. В этих ситуациях используйте специальные ARIA-роли. Лучше всего задавать их семантически нейтральным <div> и <span>, но бывают случаи, когда их можно использовать и для семантических тегов. Например, <p>.
Добавить к элементу роль очень просто. Задайте role с нужным значением.
<div role="status"> <!-- Изменяющееся содержимое --></div>
<div role="status">
<!-- Изменяющееся содержимое -->
</div>
Чаще всего используют роль status. Она подходит для любых изменений, о которых скринридерам не надо срочно рассказывать. Например, что письмо успешно отправлено или что покупка оплачена.
У status невысокий приоритет объявления, так как у неё по умолчанию есть свойства aria и aria. Это означает, что скринридеры расскажут обо всём, что происходит в области, но не сразу.
<form> <!-- Элементы формы для оплаты покупки --> <button type="submit" aria-describedby="success"> Оплатить </button> <span role="status" id="success"> Покупка успешно оплачена. Начинаем собирать заказ. </span></form>
<form>
<!-- Элементы формы для оплаты покупки -->
<button type="submit" aria-describedby="success">
Оплатить
</button>
<span role="status" id="success">
Покупка успешно оплачена. Начинаем собирать заказ.
</span>
</form>
log пригодится для логов. Это отдельный документ или часть страницы, где выводятся в определённой последовательности данные или информация о действиях пользователей или работы программы. Старая информация скрывается по мере добавления новой. Например, log можно добавить к истории сообщений или для списка ошибок.
log по умолчанию заданы aria и aria, поэтому скринридеры не сразу рассказывают о последних изменениях.
<h2>История сообщений</h2><div role="log"> <ul> <li> Одолжишь своего вельш-корги-кардигана до понедельника? Очень нужно. <time datetime="2077-04-21T12:09">12:09</time> </li> <li> Тебя снова взломали? <time datetime="2077-04-21T13:00">13:00</time> </li> </ul></div>
<h2>История сообщений</h2>
<div role="log">
<ul>
<li>
Одолжишь своего вельш-корги-кардигана до понедельника? Очень нужно.
<time datetime="2077-04-21T12:09">12:09</time>
</li>
<li>
Тебя снова взломали?
<time datetime="2077-04-21T13:00">13:00</time>
</li>
</ul>
</div>
Когда нужно, чтобы пользователи узнали обо всех изменениях в логах, в том числе старых, используйте aria. Если это срочные изменения, добавьте aria.
От роли marquee веет прошлым. На заре интернета было модно добавлять на сайты бегущие строки. Тогда это делали с помощью тега <marquee>. Тег устарел, но иногда нужно добавить на сайт блок с быстро изменяющейся информацией. marquee придумали как раз для таких ситуаций. Роль пригодится для блоков с биржевой информацией и каруселей.
marquee задан атрибут aria, так что у роли нет приоритета объявления. Скринридеры не расскажут об изменениях в такой области без фокуса на ней, даже если пользователи сейчас не взаимодействуют со страницей.
<h3>Текущие курсы валют</h3><ul role="marquee"> <li>999 956 $ за юань</li> <li>1 000 000 000 032 $ за докакоин</li></ul>
<h3>Текущие курсы валют</h3>
<ul role="marquee">
<li>999 956 $ за юань</li>
<li>1 000 000 000 032 $ за докакоин</li>
</ul>
Роль timer говорит сама за себя. Это любая область, в которой ведётся обратный и обычный отсчёт времени.
У timer нет приоритета объявления, по умолчанию ей задан aria. Так что с этой ролью рекомендуют дополнительно использовать aria и aria.
<div id="clock" role="timer" aria-live="polite" aria-atomic="true"> <span id="clock-mins"></span> <span id="clock-secs"></span></div>
<div id="clock" role="timer" aria-live="polite" aria-atomic="true">
<span id="clock-mins"></span>
<span id="clock-secs"></span>
</div>
Роль alert используют редко и только для срочных, важных оповещений. Например, что пропало интернет-соединение или не получилось сделать денежный перевод.
Важный момент в использовании alert — срочным может быть динамически появляющееся содержимое, а не подгружающееся. Чаще всего это ошибка к форме, которая стала видимой после нажатия на кнопку «Отправить». Ещё задавайте эту роль только тексту. Ссылки и кнопки, простите! В этот раз обойдёмся без вас.
В alert встроены атрибуты aria и aria, так что скринридеры моментально рассказывают обо всём, что происходит в области.
.show { display: inline-block;}
.show {
display: inline-block;
}
<div role="alert"> <!-- Класс .show добавляется и удаляется JavaScript--> <span class="show"> Изменения не сохранились из-за проблем с сервером. </span></div>
<div role="alert">
<!-- Класс .show добавляется и удаляется JavaScript-->
<span class="show">
Изменения не сохранились из-за проблем с сервером.
</span>
</div>
Все особенности ролей изменяющихся областей в одной таблице.
| Роль | Описание | Встроенные атрибуты |
|---|---|---|
status | Несрочные изменения. | aria, aria |
log | Изменения в логах. | aria, aria |
marquee | Сменяющаяся информация. | aria |
timer | Отсчёт времени. | aria |
alert | Срочные изменения. | aria, aria |
ARIA Notify
СкопированоARIA Notify — это API (интерфейс программирования приложения) для важных уведомлений. С его помощью изменяющееся содержимое попадает прямо в DOM без посредничества ARIA.
Метод aria принимает строку текста уведомления. Дополнительно можете добавить опции high для срочных уведомлений и normal для всех остальных (используется по умолчанию).
document.ariaNotify("Письмо отправлено.", { "priority": "normal" })
document.ariaNotify("Письмо отправлено.", { "priority": "normal" })
Песочница
СкопированоВ демке собраны все роли и атрибуты изменяющихся областей. Можете выбрать нужные сочетания и послушать, как скринридеры объявляют в этом случае содержимое. Его можно добавлять и удалять.