Make JavaScript updates (including AJAX) accessible

9 September 2013

Towards the end of Laura Kalbag's excellent talk on accessibility at last night's FrontEndLondon meetup, a question was asked about how to deal with when JavaScript updated the page.  There wasn't really time to go through it, and really its something best shown with a demo.

There are a couple of aria attributes that are well-supported by screenreaders that can help you out with this.

Here's a pared-down example of an implementation.

<ul aria-live="assertive" aria-relevant="all" id="list">
    <li>item one</li>
    <li>item two</li>
<button type="button" id="addnew" aria-controls="list">Add new item</button>
<button type="button" id="remove" aria-controls="list">Remove last item</button>

//assuming jquery

window.onload = function () {
//NB: this is not how you'd build a module in production, its pared down for clarity of what's important.
var mymodule = (function ($) {
  return function () {
    var addBtn = $("#addnew");
    var removeBtn = $("#remove")
    var list = $("#list");

    var addItem = function () {
      var xhr = $.ajax({url:"/"});
      list.attr("aria-busy", "true");//apply the busy state
      xhr.done(function (data) {
        var newLi = $('
  • New item
  • '); list.append(newLi); }); () { alert("something went wrong"); }); xhr.always(function () { list.attr("aria-busy", "false"); // don't just do this on success, do it always. your server may return an error. }); }; var removeItem = function (e) { var c = list.children(); c.eq(c.length - 1).remove(); }; addBtn.on("click", addItem); removeBtn.on("click", removeItem); } }(jQuery));

    If you run this example, you will notice that the announcement just consists of the new text. For example, you'd hear "Remove last item." then upon activating it you'd hear "item two". There's no further information such as whether the content has been added or removed. If your feature doesn't make sense without that extra bit of information, then you'd want to do something slightly different, where you update the text of a separate element with aria-live set on it with the fuller message.

    Note that 'add' makes an ajax call, but 'remove' just acts on the DOM. Both get announced.

    There are 2 attributes you will definitely need:

    There are 3 attributes you will possibly need:

    These aria attributes sit on block-level elements that contain the content that is going to change.


    Indicates that this is a part of the page that will get updated even after the page has loaded.

    When the element with this attribute has a content change, the assistive technology automatically picks up on it and deals with it without you having to do anything more.

    Its values are: off (ignore changes, default), polite (announce new content when you've run out of content to read), assertive (announce new content at the first opportunity).

    There used to be aria-live="rude" but it was so badly abused and disrupted users so much that it got dropped.  Be considerate when using assertive.  Confirming a save action probably justifies assertive.  An updated news feed is better being polite.


    Indicates that an element's content is in the process of changing.  Its values are true or false.

    Unlike aria-live, this one is something that you'd dynamically update from JavaScript.

    A good rule of thumb is that if you are showing a spinner over something to indicate activity, you probably also want to set aria-busy to true on the container element for the area is getting updated, and to false when it hides


    Should the whole area be announced when just one part of if has changed? Or just the bit that changed?

    Its values are true (announce the whole region when something changes) or false (default).

    TIP: if your area's text is not being read out when it updates, sometimes adding aria-atomic="true" fixes it.


    What type of changes should be announced?

    Values are one or more of additions (meaning elements being added), text, removals (again, of elements) and all, separated by a space.  Default is aria-relevant="additions text"


    Indicates a relationship between an interactive element (like a link or button) and the element it changes.

    Value is ID of the controlled element.


    Mac: System Preferences > Universal Access

    From there you can check the box to Show Universal Access status in the menu bar for easier access.

    You will get best results from working with Safari or Chrome rather than Firefox on Mac.

    Windows: Visit Freedom Scientific and download the demo version of JAWS.  After 20 minutes of use, it stops and you have to reboot to get it going again, so plan your test time accordingly.

    NVDA is another free, popular screenreader but has fewer functions than the longer-established, paid-for JAWS.