DEV Community

Cover image for Aria-live in JavaScript Frameworks
Mark Steadman
Mark Steadman

Posted on

Aria-live in JavaScript Frameworks

The aria-live attribute makes it possible for assistive technology users (screen readers in particular) to be notified when status messages, errors, or page updates have happened. Examples would be a "you are logged in!" toast message or a global error message stating "There are multiple issues with the form, please correct".

The content within an aria-live region is automatically read by assistive technology when new content appears in that region. This allows for dynamic announcements or state changes on the site to be read to assistive technology users which makes the experience in your application easier to use.

The Problem in JavaScript Frameworks

Aria-live regions in general have struggled to announce properly in JavaScript frameworks due to the dynamic nature of them. Asynchronously adding content into the DOM makes it hard for assistive technologies to pick up the region and announce it.

Initially developers would create live regions that were reusable components (see example below), which would include the aria-live attribute. The component would then dynamically be added into the DOM, and render the message.

The result, assistive technology greatly struggled to read aria-live when it is not in the DOM on load of the page. The announcement was very inconsistent, and more often than not would not even be read.

Angular reusable component

     @Component({
        selector: 'toast-message-component',
        template: `<div aria-live="assertive">
                   <span className="alertText">
                   {this.props.liveText}
                   </span>
                   </div>`
      })

Enter fullscreen mode Exit fullscreen mode

The Solutions

Luckily, over the past few years a few different types of solutions that are proven to work effectively have come to light that have greatly improved the use of aria-live in JavaScript frameworks.

Live Regions on Load

One sure fire way to ensure live region properly announces is to always include it in your application.

By simply including a live region container across your entire application or having the component render the live region on load, and then dynamically adding the message or content you wish too have announced then it will properly announce every single time!

React on load aria-live component

function LiveAnnouncer({ liveText }) {
  const setText = (text) => {
    if (liveRef.current) {
      const newText = document.createElement("span");
      newText.innerHTML = text;
      liveRef.current.appendChild(newText);
    }
  };
  const clearText = () => {
    if (liveRef.current) {
      liveRef.current.innerHTML = "";
    }
  };
  useEffect(() => {
    clearText();
    setTimeout(() => {
      setText(liveText);
    }, 50);
  }, [liveText]);
  return (
    <div aria-live="assertive" />
  );
}
Enter fullscreen mode Exit fullscreen mode

Lazy Load Components

Lazy loading a component allows the JavaScript payload of a component or a page to load properly. This is why code splitting and lazy loading is extremely useful. It is also extremely useful from an accessibility perspective because it gives live regions time to fully render and therefore giving the screen reader time to catch-up.

Vue lazy loaded component

<template>
  <div class="container"> 
    <lazy-Live />
  </div>
</template>

<script>
export default {
  components: {
    lazyLive: () => import('ToastAnnouncement.vue')
  }
}
)
Enter fullscreen mode Exit fullscreen mode

If you are using React, you can also lazy load components in tandem with Suspense. Suspense accepts a fallback component which allows you to display any React component as a loading state.

React lazy loaded component with suspense

import React, { lazy, Suspense } from 'react';

const ToastAnnouncement = lazy(() => import('./ToastAnnouncement'));

const loader = () => <p>Please wait...</p>;

const Homepage = () => (
<div>
  <Suspense fallback={loader()}>
    <ToastAnnouncement />
  </Suspense>
</div>
)
Enter fullscreen mode Exit fullscreen mode

Open Source Libraries

Open source libraries are another great solution for fixing aria-live issues in JavaScript frameworks. They are quick and very easy to setup within your application.

Most solutions in these packages are similar to the previous sections solution, where the aria-live region is on the page the whole time, and it switches out what is announced, Or it makes use of lazy loading.

Here is a list of known live region node packages by framework:

React

Angular

Vue

In Summary

When dealing with aria-live regions in JavaScript frameworks it can be tricky to ensure the regions are announcing properly. However, using any of these above methods will greatly improve the accessibility of your live regions in your JavaScript application!

Top comments (3)

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

Great article and I am always a fan of anybody trying to drill accessibility into people 😁😂.

Have a ❤🦄 and a follow!

Quick question though - in your second example are you not appending messages to the live region instead of replacing the content?

This will cause some screen readers to repeat the previous text as well as the new text.

You have the same issue on your example page at Deque I just noticed:
dequeuniversity.com/library/aria/l...

Try it with NVDA and either FireFox or Chrome - it reads everything in the box with each update rather than the last item added.

I have always believed it is nearly always better to simply replace the text in the live region? mainly due to poor support of aria-relevant or am I wrong (always happy to learn something new!)?

Collapse
 
steady5063 profile image
Mark Steadman

Hey There!

Great question. If you notice in the component there is setText() and clearText() anytime this component is called, or the props are added it will clear the live region that is added to the page, and then add it. So it doesn't actually append it is completely removing it from the live region and replacing it, which does work.

Collapse
 
grahamthedev profile image
GrahamTheDev

Doh 🤦‍♂️! How did I miss the clearText bit...I am going to blame reading it on a mobile over my inability to read code....it would be a lie but it makes me look less stupid 😜😂😂

Glad to know I am not doing it wrong though! Did you look at the page I linked (which I am now going to read to check it doesn’t have a disclaimer saying “support is patchy for this implementation” seeing as I am not doing well reading things properly!)

Once again great article and thanks for pointing out my goof! ❤️