Клавиша / esc

Что такое изменяющаяся область

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

Время чтения: 13 мин

Изменяющаяся или «живая» область (live region) — это часть страницы, в которой происходят изменения, о которых автоматически расскажут пользователям скринридеры и другие вспомогательные технологии.

Раньше скринридеры не умели рассказывать о динамически изменяющемся содержимом. Например, что при отправке формы возникла ошибка или об успешном сохранении изменений. Пользователям приходилось возвращаться в начало и конец блока или целой страницы. Как вы догадались, обычно об изменениях узнавали случайно.

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

Когда и как использовать

Скопировано

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

  • ошибки к полям и целым формам;
  • сообщения об успешности действий пользователя;
  • оповещения;
  • прогресс загрузки или удаления;
  • логи, чаты с сообщениями, списки друзей, фид;
  • таймеры;
  • биржевые тикеры, бегущие строки, прогнозы погоды, карусели.

Основные правила использования изменяющихся областей:

  • В области находится только изменяющееся содержимое. Не делайте «живой» всю страницу.
  • Не делайте изменяющимися областями все части страницы с изменениями. Например, об ошибке в форме рассказать важно, а об изменении названия кнопки не всегда.
  • Следите за встроенными в теги свойствами и узнавайте, что делают другие ARIA-атрибуты. Например, достаточно задать кнопке, которая раскрывает меню, состояние aria-expanded.
  • Изменяющимися областями могут быть интерактивные элементы и простой текст. Исключение — arial-live="assertive" и роль alert. Их добавляют только для текста.
  • Хорошо связывать элемент, который приводит к изменениям на странице, и изменяющуюся область. Используйте aria-describedby или aria-labelledby.

Чтобы изменяющаяся область заработала, в ней должно что-то поменяться. Если точнее, должен измениться DOM (Document Object Model) и дерево доступности (Accessibility Tree). Есть несколько способов это сделать. Спойлер: во всех случаях нужен JavaScript 🪄

Первый способ — удалить или добавить целый элемент в DOM. Это можно сделать с помощью JavaScript-методов манипуляции с элементами из DOM — .createElement, .innerHTML и так далее. Подробнее в статье «Element».

Также можно менять значения display с none на block, visibility с hidden на visible и удалять и добавлять HTML-атрибут hidden.

Второй способ — удалить или добавить только содержимое элемента, при этом сам элемент всегда есть на странице. Тут снова поможет JavaScript, особенно метод .textContent.

Другой путь — добавлять новое содержимое по мере загрузки страницы или после перезагрузки.

Наконец, можно добавить ARIA-атрибут или роль интерактивной области к элементу, когда это нужно, и затем удалить. Так часто поступают с таймерами.

Как добавить

Скопировано

Есть три способа.

  • Использовать теги, в которые уже встроены нужные роли и свойства, — <output> и <progress>.
  • Задействовать атрибуты aria-live и опционально aria-busyaria-atomic или aria-relevant.
  • Добавить к тегам специальные ARIA-роли status, log, marquee, timer и alert.

Обычно проблему доступности динамически изменяющегося контента решают с помощью 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> --><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-busy="true" и свяжите её с прогресс-баром с помощью aria-describedby.

        
          
          <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-live со значением 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-liveassertive. Используйте его для срочных оповещений, о которых надо рассказать прямо сейчас. Так что применяйте aria-live="assertive" с осторожностью. Значение подойдёт для критически важных ошибок, которые прерывают действия пользователей. Например, когда пропало интернет-соединение и пользователь из-за этого не может отправить заявку.

        
          
          <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-liveoff. Такие области всё ещё изменяющиеся, но пользователи узнают об изменениях в них только если сделали фокус на них или элементах внутри.

Остальные ARIA-атрибуты изменяющихся областей необязательные и нужны в особых ситуациях.

aria-busy помогает скринридерам понять, что элемент сейчас изменяется и нужно пока ни о чём не рассказывать и подождать окончания этих изменений.

Атрибут пригодится для сложных компонентов. Например, для фида с ролью feed или ленты новостей как в социальных сетях. В этом случае можно задать на время загрузки aria-busy="true" всей области с новостями, а после удалить. Так скринридеры расскажут об изменениях только тогда, когда пользователь нажмёт кнопку «Обновить ленту» и все элементы загрузятся.

        
          
          <div role="feed" aria-busy="true">  <!-- Тут загружаются посты --></div>
          <div role="feed" aria-busy="true">
  <!-- Тут загружаются посты -->
</div>

        
        
          
        
      

aria-atomic рассказывает скринридеру об объёме изменений, о которых надо рассказать. Это вся изменяющаяся область или её часть. Атрибут крайне полезен для таймеров.

Если используете aria-atomic="true" в таймере, вспомогательные технологии будут правильно объявлять текущее время: сначала часы, даже если они не изменились, потом минуты. Без атрибута скринридеры расскажут только об изменившихся минутах.

        
          
          <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-relevant сообщает скринридерам, о каком типе изменений рассказать. Это может быть добавление текста или элементов, их удаление или всё вместе. Атрибут особенно полезен в случае чата в мессенджере или обновления списка друзей.

Для добавляющихся элементов используйте значение additions, для добавляющегося текста — text, для удаляющегося содержимого — removals. Когда в изменяющейся области важны все типы изменений, используйте all.

Если нужно рассказать пользователям об удалении и добавлении содержимого, можно объединить сразу два значения. В этом примере со списком друзей используем aria-relevant="additions removals".

        
          
          <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-live="polite" и aria-atomic="true". Это означает, что скринридеры расскажут обо всём, что происходит в области, но не сразу.

        
          
          <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-live="polite" и aria-atomic="false", поэтому скринридеры не сразу рассказывают о последних изменениях.

        
          
          <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-atomic="true". Если это срочные изменения, добавьте aria-live="assertive".

От роли marquee веет прошлым. На заре интернета было модно добавлять на сайты бегущие строки. Тогда это делали с помощью тега <marquee>. Тег устарел, но иногда нужно добавить на сайт блок с быстро изменяющейся информацией. marquee придумали как раз для таких ситуаций. Роль пригодится для блоков с биржевой информацией и каруселей.

marquee задан атрибут aria-live="off", так что у роли нет приоритета объявления. Скринридеры не расскажут об изменениях в такой области без фокуса на ней, даже если пользователи сейчас не взаимодействуют со страницей.

        
          
          <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-live="off". Так что с этой ролью рекомендуют дополнительно использовать aria-live="polite" и aria-atomic="true".

        
          
          <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-live="assertive" и aria-atomic="true", так что скринридеры моментально рассказывают обо всём, что происходит в области.

        
          
          .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-live="polite", aria-atomic="true"
log Изменения в логах. aria-live="polite", aria-atomic="false"
marquee Сменяющаяся информация. aria-live="off"
timer Отсчёт времени. aria-live="off"
alert Срочные изменения. aria-live="assertive", aria-atomic="true"

Песочница

Скопировано

В демке собраны все роли и атрибуты изменяющихся областей. Можете выбрать нужные сочетания и послушать, как скринридеры объявляют в этом случае содержимое. Его можно добавлять и удалять.

Открыть демо в новой вкладке