Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move the aria-live example in page #37167

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
325 changes: 309 additions & 16 deletions files/en-us/learn/accessibility/wai-aria_basics/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,9 @@ The improved semantics of the search form have shown what is made possible when

Content loaded into the DOM can be easily accessed using a screen reader, from textual content to alternative text attached to images. Traditional static websites with largely text content are therefore easy to make accessible for people with visual impairments.

The problem is that modern web apps are often not just static text — they often update parts of the page by fetching new content from the server and updating the DOM. These are sometimes referred to as **live regions**.
The problem is that modern web apps are often not just static text — they often update parts of the page by fetching new content from the server (in this example we are using a static array of quotes) and updating the DOM. These are sometimes referred to as **live regions**.

Let's look at a quick example — see [`aria-no-live.html`](https://github.com/mdn/learning-area/blob/main/accessibility/aria/aria-no-live.html) (also [see it running live](https://mdn.github.io/learning-area/accessibility/aria/aria-no-live.html)). In this example, we have a simple random quote box:

```html
```html live-sample___aria-no-live
<section>
<h1>Random quote</h1>
<blockquote>
Expand All @@ -220,12 +218,62 @@ Let's look at a quick example — see [`aria-no-live.html`](https://github.com/m
</section>
```

Our JavaScript uses the {{domxref("Window.fetch", "fetch()")}} API to load a JSON file via containing a series of random quotes and their authors. Once that is done, we start up a {{domxref("Window.setInterval", "setInterval()")}} loop that loads a new random quote into the quote box every 10 seconds:
```css live-sample___aria-no-live
html {
font-family: sans-serif;
}

h1 {
letter-spacing: 2px;
}

```js
const intervalID = setInterval(showQuote, 10000);
p {
line-height: 1.6;
}

section {
padding: 10px;
width: calc(100% - 20px);
background: #666;
text-shadow: 1px 1px 1px black;
color: white;
min-height: 160px;
}
```

```js live-sample___aria-no-live
let quotes = [
{
quote:
"Every child is an artist. The problem is how to remain an artist once he grows up.",
author: "Pablo Picasso",
},
{
quote:
"You can never cross the ocean until you have the courage to lose sight of the shore.",
author: "Christopher Columbus",
},
{
quote:
"I love deadlines. I love the whooshing noise they make as they go by.",
author: "Douglas Adams",
},
];
```

```js live-sample___aria-no-live
const quotePara = document.querySelector("section p");

window.setInterval(showQuote, 10000);

function showQuote() {
let random = Math.floor(Math.random() * quotes.length);
quotePara.textContent = `${quotes[random].quote} -- ${quotes[random].author}`;
}
```

{{EmbedLiveSample("aria-no-live", "100", "180")}}

This works OK, but it is not good for accessibility — the content update is not detected by screen readers, so their users would not know what is going on. This is a fairly trivial example, but just imagine if you were creating a complex UI with lots of constantly updating content, like a chat room, or a strategy game UI, or a live updating shopping cart display — it would be impossible to use the app in any effective way without some kind of way of alerting the user to the updates.

WAI-ARIA, fortunately, provides a useful mechanism to provide these alerts — the [`aria-live`](/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-live) property. Applying this to an element causes screen readers to read out the content that is updated. How urgently the content is read out depends on the attribute value:
Expand All @@ -237,17 +285,14 @@ WAI-ARIA, fortunately, provides a useful mechanism to provide these alerts — t
- `assertive`
- : Updates should be announced to the user as soon as possible.

We'd like you to take a copy of [`aria-no-live.html`](https://github.com/mdn/learning-area/blob/main/accessibility/aria/aria-no-live.html) and [`quotes.json`](https://github.com/mdn/learning-area/blob/main/accessibility/aria/quotes.json), and update your `<section>` opening tag as follows:
Here we update the `<section>` opening tag as follows:

```html
<section aria-live="assertive">…</section>
```

This will cause a screen reader to read out the content as it is updated.

> [!NOTE]
> Most browsers will throw a security exception if you try to make an HTTP request from a `file://` URL, e.g. if you just load the file by loading it directly into the browser (via double clicking, etc.). See [how to set up a local testing server](/en-US/docs/Learn/Common_questions/Tools_and_setup/set_up_a_local_testing_server).

There is an additional consideration here — only the bit of text that updates is read out. It might be nice if we always read out the heading too, so the user can remember what is being read out. To do this, we can add the [`aria-atomic`](/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-atomic) property to the section. Update your `<section>` opening tag again, like so:

```html
Expand All @@ -256,8 +301,70 @@ There is an additional consideration here — only the bit of text that updates

The `aria-atomic="true"` attribute tells screen readers to read out the entire element contents as one atomic unit, not just the bits that were updated.

> [!NOTE]
> You can see the finished example at [`aria-live.html`](https://github.com/mdn/learning-area/blob/main/accessibility/aria/aria-live.html) ([see it running live](https://mdn.github.io/learning-area/accessibility/aria/aria-live.html)).
```html live-sample___aria-live
<section aria-live="assertive" aria-atomic="true">
<h1>Random quote</h1>
<blockquote>
<p></p>
</blockquote>
</section>
```

```css live-sample___aria-live
html {
font-family: sans-serif;
}

h1 {
letter-spacing: 2px;
}

p {
line-height: 1.6;
}

section {
padding: 10px;
width: calc(100% - 20px);
background: #666;
text-shadow: 1px 1px 1px black;
color: white;
min-height: 160px;
}
```

```js live-sample___aria-live
let quotes = [
{
quote:
"Every child is an artist. The problem is how to remain an artist once he grows up.",
author: "Pablo Picasso",
},
{
quote:
"You can never cross the ocean until you have the courage to lose sight of the shore.",
author: "Christopher Columbus",
},
{
quote:
"I love deadlines. I love the whooshing noise they make as they go by.",
author: "Douglas Adams",
},
];
```

```js live-sample___aria-live
const quotePara = document.querySelector("section p");

window.setInterval(showQuote, 10000);

function showQuote() {
let random = Math.floor(Math.random() * quotes.length);
quotePara.textContent = `${quotes[random].quote} -- ${quotes[random].author}`;
}
```

{{EmbedLiveSample("aria-live", "100", "180")}}

> [!NOTE]
> The [`aria-relevant`](/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-relevant) property is also quite useful for controlling what gets read out when a live region is updated. You can for example only get content additions or removals read out.
Expand Down Expand Up @@ -383,11 +490,197 @@ Now when you try this using a screen reader, you'll have buttons be reported usi

There are a whole host of other [roles](/en-US/docs/Web/Accessibility/ARIA/Roles) that can identify non-semantic element structures as common UI features that go beyond what's available in standard HTML, for example [`combobox`](/en-US/docs/Web/Accessibility/ARIA/Roles/combobox_role), [`slider`](/en-US/docs/Web/Accessibility/ARIA/Roles/slider_role), [`tabpanel`](/en-US/docs/Web/Accessibility/ARIA/Roles/tabpanel_role), [`tree`](/en-US/docs/Web/Accessibility/ARIA/Roles/tree_role). You can see several useful examples in the [Deque university code library](https://dequeuniversity.com/library/) to give you an idea of how such controls can be made accessible.

Let's go through an example of our own. We'll return to our simple absolutely-positioned tabbed interface (see [Hiding things](/en-US/docs/Learn/Accessibility/CSS_and_JavaScript#hiding_things) in our CSS and JavaScript accessibility article), which you can find at [Tabbed info box example](https://mdn.github.io/learning-area/css/css-layout/practical-positioning-examples/tabbed-info-box.html) (see [source code](https://github.com/mdn/learning-area/blob/main/css/css-layout/practical-positioning-examples/tabbed-info-box.html)).
Let's go through an example of our own. We'll return to our simple absolutely-positioned tabbed interface (see [Hiding things](/en-US/docs/Learn/Accessibility/CSS_and_JavaScript#hiding_things) in our CSS and JavaScript accessibility article), which you can find at [Tabbed info box example](/en-US/docs/Learn/CSS/CSS_layout/Practical_positioning_examples#a_fixed_position_tabbed_info-box).

This example as-is works fine in terms of keyboard accessibility — you can happily tab between the different tabs and select them to show the tab contents. It is also fairly accessible too — you can scroll through the content and use the headings to navigate, even if you can't see what is happening on screen. It is however not that obvious what the content is — a screen reader currently reports the content as a list of links, and some content with three headings. It doesn't give you any idea of what the relationship is between the content. Giving the user more clues as to the structure of the content is always good.

To improve things, we've created a new version of the example called [`aria-tabbed-info-box.html`](https://github.com/mdn/learning-area/blob/main/accessibility/aria/aria-tabbed-info-box.html) ([see it running live](https://mdn.github.io/learning-area/accessibility/aria/aria-tabbed-info-box.html)). We've updated the structure of the tabbed interface like so:
To improve things, we've created a new version of the example.

```html live-sample___aria-tabbed-info-box
<section class="info-box">
<ul role="tablist">
<li
class="active"
role="tab"
aria-selected="true"
aria-setsize="3"
aria-posinset="1"
tabindex="0">
Tab 1
</li>
<li
role="tab"
aria-selected="false"
aria-setsize="3"
aria-posinset="2"
tabindex="0">
Tab 2
</li>
<li
role="tab"
aria-selected="false"
aria-setsize="3"
aria-posinset="3"
tabindex="0">
Tab 3
</li>
</ul>
<div class="panels">
<article class="active-panel" role="tabpanel" aria-hidden="false">
<h2>The first tab</h2>

<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque
turpis nibh, porttitor nec venenatis eu, pulvinar in augue. Vestibulum
et orci scelerisque, vulputate tellus quis, lobortis dui. Vivamus varius
libero at ipsum mattis efficitur ut nec nisl. Nullam eget tincidunt
metus. Donec ultrices, urna maximus consequat aliquet, dui neque
eleifend lorem, a auctor libero turpis at sem. Aliquam ut porttitor
urna. Nulla facilisi.
</p>
</article>
<article role="tabpanel" aria-hidden="true">
<h2>The second tab</h2>

<p>
This tab hasn't got any Lorem Ipsum in it. But the content isn't very
exciting all the same.
</p>
</article>
<article role="tabpanel" aria-hidden="true">
<h2>The third tab</h2>

<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque
turpis nibh, porttitor nec venenatis eu, pulvinar in augue. And now an
ordered list: how exciting!
</p>

<ol>
<li>dui neque eleifend lorem, a auctor libero turpis at sem.</li>
<li>Aliquam ut porttitor urna.</li>
<li>Nulla facilisi</li>
</ol>
</article>
</div>
</section>
```

```css hidden live-sample___aria-tabbed-info-box
/* General setup */
body {
font-family: sans-serif;
box-sizing: border-box;
margin: 0;
}

/* info-box setup */

.info-box {
width: calc(100% - 20px);
height: 400px;
margin: 0 auto;
}

/* styling info-box tabs */

ul[role="tablist"] {
padding-left: 0;
margin-top: 0;
}

li[role="tab"] {
float: left;
list-style-type: none;
width: calc(100% / 3);
display: inline-block;
line-height: 3;
background-color: red;
color: black;
text-align: center;
}

li[role="tab"]:focus,
li[role="tab"]:hover {
background-color: #a60000;
color: white;
}

li[role="tab"].active {
background-color: #a60000;
color: white;
}

/* styling info-box panels */

.info-box .panels {
clear: both;
position: relative;
height: 352px;
}

.info-box article {
background-color: #a60000;
color: white;
position: absolute;
padding: 10px;
height: 352px;
top: 0;
left: 0;
}

.info-box .active-panel {
z-index: 1;
}
```

```js live-sample___aria-tabbed-info-box
let tabs = document.querySelectorAll(".info-box li");
let panels = document.querySelectorAll(".info-box article");

for (let i = 0; i < tabs.length; i++) {
let tab = tabs[i];
setTabHandler(tab);
}

function setTab(e) {
if (e.type === "keypress" && e.keyCode !== 13) {
return;
}

let tabPos = Number(this.getAttribute("aria-posinset")) - 1;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].getAttribute("class")) {
tabs[i].removeAttribute("class");
}

tabs[i].setAttribute("aria-selected", "false");
}

this.setAttribute("class", "active");
this.setAttribute("aria-selected", "true");

for (let i = 0; i < panels.length; i++) {
if (panels[i].getAttribute("class")) {
panels[i].removeAttribute("class");
}

panels[i].setAttribute("aria-hidden", "true");
}

panels[tabPos].setAttribute("class", "active-panel");
panels[tabPos].setAttribute("aria-hidden", "false");
}

function setTabHandler(tab) {
tab.addEventListener("click", setTab);
tab.addEventListener("keypress", setTab);
}
```

{{EmbedLiveSample("aria-tabbed-info-box", "100", "420")}}

We've updated the structure of the tabbed interface like so:

```html
<ul role="tablist">
Expand Down Expand Up @@ -425,7 +718,7 @@ To improve things, we've created a new version of the example called [`aria-tabb
```

> [!NOTE]
> The most striking change here is that we've removed the links that were originally present in the example, and just used the list items as the tabs — this was done because it makes things less confusing for screen reader users (the links don't really take you anywhere; they just change the view), and it allows the setsize/position in set features to work better — when these were put on the links, the browser kept reporting "1 of 1" all the time, not "1 of 3", "2 of 3", etc.
> The most striking change here is that we've removed the links that were originally present in the [example](/en-US/docs/Learn/CSS/CSS_layout/Practical_positioning_examples#a_fixed_position_tabbed_info-box), and just used the list items as the tabs — this was done because it makes things less confusing for screen reader users (the links don't really take you anywhere; they just change the view), and it allows the setsize/position in set features to work better — when these were put on the links, the browser kept reporting "1 of 1" all the time, not "1 of 3", "2 of 3", etc.

ARIA features used include:

Expand Down
Loading