It's official: Level Access and eSSENTIAL Accessibility are becoming one! Read the Press Release.
A laptop showing colorful lines of code on the screen

This post is one in a series of posts on accessible rich internet applications (ARIA) and the specification by the same name.  One challenge with composite widgets (widgets that are made up of other widgets) is properly exposing programmatic focus to assistive technology.  The widget with programmatic focus is the widget where commands are sent by assistive technology such as screen readers and speech recognition software.  It is also the magnified area where screen magnification software displays for users with low vision. Two solutions will be discussed and an explanation of the necessity of these approaches are provided with sample code.

One attribute may not sound like much, but in the case of aria-activedescendant, it controls a lot of feedback for screen reader users. Similarly, there are only two ways to make ARIA Widgets programmatically focusable for screen reader users; one of which requires the use of aria-activedescendant.

Without a clear understanding of these two methods and where aria-activedescendant fits in the mix, it is often unclear for developers why keyboard accessibility does not always match screen reader accessibility.

For example, when ARIA role mappings do not coincide with the same elements used for programmatic focus handling, the accessible feedback provided by the role is often ignored by the screen reader.

This applies to all of the following ARIA Widget types:

  • ARIA Comboboxes
  • ARIA Editable Grids
  • ARIA Listboxs
  • ARIA Menus and MenuBars
  • ARIA Tab Lists
  • ARIA Trees and TreeGrids
  • ARIA ToolBars

(Reference: http://www.w3.org/TR/wai-aria/roles)

First, a Bit of Background

It’s important to note that all of these Widget types should only have one tab stop in the keyboard tab order. The arrow keys and related keystrokes should then control focus within these Widgets to move focus between related nodes. For example, only one container or node should include tabindex=”0″ to make it focusable in the tab order, and all other nodes should include tabindex=”-1″ to ensure programmatic focusability while removing them from the tab order.

“When the container or its active descendant has focus, the user may navigate through the container by pressing additional keys, such as the arrow keys, to change the currently active descendant. Any additional press of the main navigation key (generally the TAB key) will move out of the container to the next widget.”
(Reference: http://www.w3.org/TR/wai-aria/usage#managingfocus)

An active descendant or ‘node’ as described here, is defined as a child element that contains a supporting role for that Widget type. For instance, a Widget container for a Listbox includes role=”listbox”, and all relevant child nodes include role=”option”.

This is important because programmatic focusability must always match the element that includes the supporting role, otherwise the screen reader feedback will not match the element that has focus. Meaning, keyboard focus must always be set on elements that have a defined role to ensure proper announcement for screen reader users.

How to Ensure ARIA Widget Accessibility for Screen Reader Users

The first way to ensure ARIA Widget accessibility for screen reader users is to set focus on the Widget child nodes individually. In the case of the ARIA Listbox Widget, this means setting focus on the elements with role=”option” so that only one is in the tab order at a time, and using the arrow plus related keys to move focus between each Widget node.
(Example: http://whatsock.com/tsg/Coding%20Arena/ARIA%20Listboxes/Standard/demo.htm)

The second way to ensure ARIA Widget accessibility for screen reader users, is to set focus to the Widget container, and to use the aria-activedescendant attribute to dynamically point to the ID of the selected child node. In the case of an ARIA Tree for example, this would mean setting focus to the top level container that includes role=”tree”. (An ARIA Tree construct must only ever have one instance of role=tree within it, all sub branches must be defined using role=”group”)

In a case such as this, the element that includes role=”tree” would also include tabindex=”0″ to make it focusable in the tab order, and the arrow keys should dynamically update the aria-activedescendant attribute within the same element so that it points to the ID of the currently active child node that includes role=”treeitem”.

Now That It’s Clear What the aria-activedescendant Attribute Does…

Here is the interesting bit, which is likely to confuse developers working on cross browser compatible solutions.

After setting focus to a Widget container and using the arrow keys to change selection by updating the aria-activedescendant attribute, the focus rectangle in Windows will not move and will not reflect the currently active child node as the aria-activedescendant attribute value changes.

However, if you perform the same actions on the Mac in Safari using VoiceOver, the visual focus rectangle will actually change automatically to reflect the child node that is being referenced by the aria-activedescendant attribute.

It’s important to note though, even though focus appears to be moving on the Mac when the arrow keys change the aria-activedescendant value, ‘focus’ events if bound on the child nodes themselves, will not fire. This is strictly a visual effect.

This can be tested using the following code:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
     <title> Demo </title>
     <script type="text/javascript"
       src="http://whatsock.com/jquery/js/jquery-1.8.3.min.js"></script>
     <script type="text/javascript">
       $(function(){
       var tis = ['ti1', 'ti2', 'ti3'], cur = 0,
       tree = $('#testTree');
       tree.bind('keydown', function(ev){
       var k = ev.which || ev.keyCode;
       if (k == 38 || k == 40){
       if (k == 38 && cur > 0) cur--;
       else if (k == 40 && cur < (tis.length - 1)) cur++;
       $('div[role="treeitem"][aria-selected="true"]')
        .attr('aria-selected', 'false');
        $('#' + tis[cur]).attr('aria-selected', 'true');
        tree.attr('aria-activedescendant', tis[cur]);
        ev.preventDefault();
        }
        });
        // Set a focus event on the third tree item node to test firing.
       $('#ti3').bind('focus', function(ev){
       alert('The third node has focus');
       });
       });
     </script>
  </head>
  <body>
    <div>
      <div id="testTree" tabindex="0" role="tree" aria-label="Test" 
           aria-owns="ti1 ti2 ti3" aria-activedescendant="ti1">
        <div tabindex="-1" role="treeitem" aria-selected="true" id="ti1">
          Item One
        </div>
        <div tabindex="-1" role="treeitem" aria-selected="false" id="ti2">
          Item Two
        </div>
        <div tabindex="-1" role="treeitem" aria-selected="false" id="ti3">
          Item Three
        </div>
      </div>
    </div>
  </body>
</html>


Regarding levels of screen reader support and feedback, to see how the use of aria-activedescendant differs when compared to the method of setting focus to individual child nodes, view the demo at

http://whatsock.com/tsg/Coding%20Arena/ARIA%20Trees/Tree%20(External%20XML)/demo.htm