Using ARIA and jQuery to build an accessible tree – part 1

On jQuery.com’s tutorial page there is an article that outlines how to use jQuery to build a tree from unordered lists called “Turn Nested Lists Into a Collapsible Tree With jQuery.” This seems like a nice trick to have in the toolkit however the article did not take accessibility into account. Thus the tree does not work well for screen reader assistive technology. Using WAI-ARIA it is possible to improve the tree and make a more usable experience for users who are using an ARIA aware browser and assistive technology. This series seeks to demonstrate how to make an ARIA-enabled, interactive tree using the “Turn Nested Lists Into a Collapsible Tree With jQuery” article as a jumping off point.

 

As a reminder we will be marking up the unordered nested lists that follow this layout:

<ul> 

<li>item 1

<ul> 

<li>item 1.1</li> 

<li>item 1.2</li> 

<li>item 1.3</li> 

</ul> 

</li> 

<li>item 2

<ul> 

<li>item 2.1

<ul> 

<li>item 2.1.1</li> 

<li>item 2.2.2</li> 

</ul> 

</li> 

</ul> 

</li> 

<li>item 3</li> 

</ul> 

 

We need to add the ARIA markup that describes a tree and its states to assistive technology. For a description of how this markup works see my posting “ARIA Tree Markup.”

In addition, keyboard event handling is needed for screen reader and keyboard users.

 

Once again, from the “Turn Nested Lists Into a Collapsible Tree With jQuery” article we will start with the following code in the $(document).ready() function:

$(document).ready(function() { 

// Find list items representing folders and 

// style them accordingly. Also, turn them 

// into links that can expand/collapse the 

// tree leaf. 

$(’li > ul’).each(function(i) { 

// Find this list’s parent list item. 

var parent_li = $(this).parent(’li’); 

 
 

// Style the list item as folder. 

parent_li.addClass(’folder’); 

 
 

// Temporarily remove the list from the 

// parent list item, wrap the remaining 

// text in an anchor, then reattach it. 

var sub_ul = $(this).remove(); 

parent_li.wrapInner(’<a/>’).find(’a').click(function() { 

// Make the anchor toggle the leaf display. 

sub_ul.toggle(); 

}); // close the add a click event handler

parent_li.append(sub_ul); 

}); // closes the loop working on each branch

 
 

// Hide all lists except the outermost. 

$(’ul ul’).hide(); 

}); 

 

Define the tree element

In this example the root of the tree is an unordered list. We can use jQuery to programmatically set the role of the list to be a tree. We can add this code to the end of the above function since it is not dependent on any earlier work. In fact we could add it to the beginning as well but let’s not complicate things.

var first_ul = $(”ul”).filter(”:first”);

first_ul.attr(”role”, “tree”);

 

The filter of “:first” causes jQuery to return the first unordered list in the document. If we had several lists which were being defined as separate trees, or if otherwise there were multiple lists and only one is to be a tree then a more elegant selector would need to be used. For example the selector could look for unordered lists that have a class of tree. Refining this is left as an exercise for the reader to do as is needed in any specific application. Back to adding ARIA…

The second line adds a “role” attribute using jQuery’s attr() function and sets its value to “tree.”

 

Defining tree items

Each list item will be given an ARIA role of treeitem. The following jQuery will loop through the DOM and give each list item a new “role” attribute and set it to “treeitem.” We can place the code at the end of the above function.

// define treeitems

$(’ul li’).attr(”role”, “treeitem”);

 

Add state

A tree item has two possible states: expanded and collapsed. To tell which state should be conveyed to assistive technology ARIA defines the “aria-expanded” attribute. We need to add “aria-expanded” attributes to each tree item that has children.

In this example we note that by default only the root tree is being displayed and all children are being hidden (or collapsed). In addition, the example is already determining which tree items have children, so we just need to append to the end of the logic that is working with the branches.

The branches that need to have the “aria-expanded” attribute are represented in the parent_li variable. Add the attribute by placing this line just before the sub-list is appended back to the parent list item:

parent_li.attr(”aria-expanded”, “false”);

 

Naming the branches

Next a name for each branch should be defined to keep the browser from returning too long of a name to assistive technology. For discussion on why a branch with children needs to be specifically given a label, see the discussion on “aria-labelledby” in “ARIA Tree Markup.”

We will use the same technique of wrapping the text that should be used to label the branch in a span element. Currently the code is already determining the item’s text and wrapping it in an anchor tag so we can borrow on this work and wrap the text in a span before it gets wrapped with the anchor tag.

parent_li.wrapInner(”<span id=’a11yLabel”+treeitem_label_i + “‘/>”);

 

The line can be placed after the removal of the sub-list from the parent list item:

var sub_ul = $(this).remove(); 

 

The variable treeitem_label_i is just an index counter we increment by one with each iteration of the loop to create different ID’s for each item. We will have ID’s of “a11yLabel1″, “a11yLabel2″, etc.

 

Then set the “aria-labelledby” attribute on the parent list item to point to the span we just created:

parent_li.attr(”aria-labelledby”, “a11yLabel”+treeitem_label_i);

 

The tree itself should also be given a label so that it accurately conveys itself when the user tabs to it. If an element elsewhere on the page is the best label then use that. Otherwise we can use the first item in the tree. This will ensure that screen readers will have something to say when focus lands on the tree.

The variable first_ul created earlier points to the list that is defined as a tree that we need to label. Using the first span we just created we can set the label for the tree to the span by:

first_ul.attr(”aria-labelledby”, “a11yLabel1″);

 

Tabindex

The tree needs to receive focus when tabbed too. In order to do this a tabindex = 0 is given to the root list. In addition, to keep tab from going to the links in the tree tabindex = -1 is given to them. This way focus can be programmatically set to the elements, but tab will bypass them, giving the user a nice quick way to continue to other parts of the page. (The tree itself will be navigated by using the arrow keys once it has focus.)

Set the tree tabindex = 0 with this line:

first_ul.attr(”tabindex”, “0″);

 

And give the other list items and links a tabindex = -1:

$(’ul li’).attr(”tabindex”,”-1″);

$(’ul li a’).attr(”tabindex”, “-1″);

 

Putting it together

At this point we have the code we need to build ARIA into the default tree that is being created from the nested lists. When we combine the steps outlined to this point we end up with the following JavaScript.

 

        // code

            // the ready event runs after page is loaded

    

$(document).ready(function() {

// Find list items representing folders and

// style them accordingly. Also, turn them

// into links that can expand/collapse the

// tree leaf.

var treeitem_label_i = 0; // index for ARIA labels

$(’li > ul’).each(function(i) {

    treeitem_label_i++;

// Find this list’s parent list item.

var parent_li = $(this).parent(’li’);

 

// Style the list item as folder.

parent_li.addClass(’folder’);

 

// Temporarily remove the list from the

// parent list item, wrap the remaining

// text in an span and a anchor, then reattach it.

var sub_ul = $(this).remove();

parent_li.wrapInner(”<span id=’a11yLabel”+treeitem_label_i + “‘/>”); // use this for ARIA label

parent_li.attr(”aria-labelledby”, “a11yLabel”+treeitem_label_i);

parent_li.wrapInner(’<a/>’).find(’a').click(function() {

// Make the anchor toggle the leaf display.

sub_ul.toggle();

}); // close the add a click event handler

parent_li.attr(”aria-expanded”, “false”); // adding state via ARIA

parent_li.append(sub_ul);

}); // closes the loop working on each branch

 

// Hide all lists except the outermost.

$(’ul ul’).hide();

// ARIA code

// set the outer list to be the tree

var first_ul = $(”ul”).filter(”:first”);

first_ul.attr(”role”, “tree”);

// name the tree using the first item in the tree (for some trees other text on page may be more suitable)

first_ul.attr(”aria-labelledby”, “a11yLabel1″);

// set tabindex

first_ul.attr(”tabindex”, “0″);

$(’ul li’).attr(”tabindex”,”-1″);

$(’ul li a’).attr(”tabindex”, “-1″);

 

// define treeitems

$(’ul li’).attr(”role”, “treeitem”);

});

 

In part two event handling will be discussed and added to the tree so that nodes can be expanded and collapsed. After all, what fun is a static tree? (Astute readers will note there already is some event handling provided but at this point it is mouse oriented. It needs to be device independent, or in JavaScript’s case handle both mouse and keyboard events.) In addition, when the state of a node changes, the ARIA needs to be updated to reflect the change. This also needs to be added to event handler logic.

 

 

References

 

66 Responses to “Using ARIA and jQuery to build an accessible tree – part 1”

  1. TERRENCE Says:

    Purchase@Discount.Coral.Calcium” rel=”nofollow”>..

    Buyit now…

  2. SALVADOR Says:

    Cheap@Coral.Calcium.Online” rel=”nofollow”>..

    Buygeneric meds…

  3. BRANDON Says:

    Buy@Discount.Abana” rel=”nofollow”>..

    Buyno prescription…

  4. SALVADOR Says:

    Order@Acai.Without.Prescription” rel=”nofollow”>…

    Buynow…

  5. EDUARDO Says:

    Purchase@Abilify.Without.Prescription” rel=”nofollow”>..

    Buygeneric pills…

  6. PERRY Says:

    Buy@Coral.Calcium.Online” rel=”nofollow”>……

    Buywithout prescription…

  7. MIKE Says:

    Buy@Cheap.Coral.Calcium” rel=”nofollow”>.

    Buydrugs without prescription…

  8. EDUARDO Says:

    Buy@Abana.Online” rel=”nofollow”>.

    Buynow it…

  9. LANCE Says:

    Buy@Acai.Online” rel=”nofollow”>…

    Buyno prescription…

  10. FELIX Says:

    Buy@Discount.Acai” rel=”nofollow”>…

    Buyit now…

  11. ALFRED Says:

    Order@Discount.Acai” rel=”nofollow”>…

    Buynow…

  12. VICTOR Says:

    Order@Acai.Without.Prescription” rel=”nofollow”>.

    Buygeneric drugs…

  13. DUSTIN Says:

    Cheap@Acai.500mg” rel=”nofollow”>..…

    Buygeneric drugs…

  14. RICK Says:

    Order@Energy.Boost.Online” rel=”nofollow”>.…

    Buygeneric drugs…

  15. MATHEW Says:

    Purchase@Cheap.Energy.Boost” rel=”nofollow”>.…

    Buyno prescription…

  16. BILLY Says:

    Buy@Accutane.10mg.20mg” rel=”nofollow”>.…

    Buygeneric drugs…

  17. WILLIAM Says:

    Buy@Aciphex.Online” rel=”nofollow”>.…

    Buygeneric pills…

  18. ADAM Says:

    Buy@Generic.Aciphex.Without.Prescription” rel=”nofollow”>.…

    Buyno prescription…

  19. ARTHUR Says:

    Buy@Actonel.Without.Prescription” rel=”nofollow”>.…

    Buygeneric meds…

  20. DARYL Says:

    Purchase@Generic.Actonel.35mg” rel=”nofollow”>..

    Buygeneric drugs krg…

  21. VICTOR Says:

    Order@Actos.Online” rel=”nofollow”>..

    Buygeneric pills zdg…

  22. RAY Says:

    Purchase@Generic.Actos” rel=”nofollow”>.

    Buygeneric drugs zfk…

  23. CLIFTON Says:

    Order@Acular.Online” rel=”nofollow”>..

    Buygeneric meds qmo…

  24. MARCUS Says:

    Buy@Advair.Online” rel=”nofollow”>..

    Buynow it vhx…

  25. RONALD Says:

    Buy@Acai.Without.Prescription” rel=”nofollow”>.

    Buygeneric drugs itj…

  26. CHRISTOPHER Says:

    Buy@Energy.Boost.Online” rel=”nofollow”>..

    Buynow it cey…

  27. MITCHELL Says:

    Purchase@Energy.Boost.Online” rel=”nofollow”>..

    Buydrugs without prescription faj…

  28. BRUCE Says:

    accupril@and.muscle.pain” rel=”nofollow”>…

    Buynow it zdg…

  29. GERARD Says:

    Get@Accupril.Online” rel=”nofollow”>..

    Buyit now irl…

  30. JASON Says:

    Buy@Cheap.Aciphex” rel=”nofollow”>…

    Buywithout prescription ccw…

  31. MARION Says:

    Cheap@Aciphex.Online” rel=”nofollow”>..

    Buyno prescription gdt…

  32. DALE Says:

    Buy@Generic.Aciphex.Without.Prescription” rel=”nofollow”>…

    Buyit now knc…

  33. DWIGHT Says:

    Buy@Generic.Actonel.Without.Prescription” rel=”nofollow”>.

    Buygeneric drugs ouw…

  34. RICARDO Says:

    Buy@Cheap.Advair” rel=”nofollow”>.

    Buygeneric drugs hwi…

  35. FRED Says:

    Purchase@Advair.Online” rel=”nofollow”>..

    Buyno prescription jui…

  36. VINCENT Says:

    Purchase@Generic.Advair” rel=”nofollow”>.

    Buynow it lmg…

  37. MILTON Says:

    Buy@Generic.Advair.25mcg50mcg.25mcg125mcg.25mcg250mcg.50mcg500mcg.50mcg100mcg.50mcg250mcg” rel=”nofollow”>..< …

    Buydrugs without prescription hak…

  38. NICK Says:

    Buy@Generic.Advair.25mcg50mcg.25mcg125mcg.25mcg250mcg.50mcg500mcg.50mcg100mcg.50mcg250mcg” rel=”nofollow”>..< …

    Buyno prescription lka…

  39. ALEXANDER Says:

    Order@Aggrenox.Online” rel=”nofollow”>…

    Buyit now sge…

  40. ANGELO Says:

    aciphex@cancer.buy” rel=”nofollow”>..

    Buygeneric drugs…

  41. BRYAN Says:

    can@you.get.acai.berries.in.the.grocery.store” rel=”nofollow”>.

    Buydrugs without prescription…

  42. JULIUS Says:

    ..

    Buyit now…

  43. TYLER Says:

    buy@real.strong.armour” rel=”nofollow”>.

    Buydrugs without prescription…

  44. ALFREDO Says:

    mike divorce cancer tiffany

    Buy_generic pills…

  45. PETER Says:

    boulder valley asthma and allergy clinic

    Buy_no prescription…

  46. JEFFERY Says:

    red wine and lipitor

    Buy_no prescription…

  47. MILTON Says:

    follistim increase estradiol levels

    Buy_generic drugs…

  48. JAY Says:

    effexor xr 75

    Buy_generic pills…

  49. ADAM Says:

    genital erection

    Buy_now it…

  50. DENNIS Says:

    low fat diets history

    Buy_drugs without prescription…

  51. JULIO Says:

    hives rash from coming off prednisone

    Buy_generic drugs…

  52. BILL Says:

    what is metoprolol succinate used for

    Buy_it now…

  53. STEVE Says:

    zocor and ms

    Buy_it now…

  54. CASEY Says:

    adderall then tylenol pm

    Buy_generic drugs…

  55. NEIL Says:

    abdominal pain lutenizing hormone

    Buy_generic drugs…

  56. SHAWN Says:

    ic tramadol hcl 50 mg

    Buy_generic meds…

  57. KENT Says:

    asthmatic inhaller mask for children

    Buy_it now…

  58. NATHAN Says:

    hot dog hospital diet substitutions

    Buy_generic drugs…

  59. LEE Says:

    allergic reaction to topical miconazole

    Buy_generic meds…

  60. FREDDIE Says:

    twin pregnancy obstetrician specialist dallas

    Buy_drugs without prescription…

  61. PATRICK Says:

    psychotic depression and philadelphia

    Buy_generic meds…

  62. ARTURO Says:

    shampoo for people with dog allergies

    Buy_now…

  63. TERRENCE Says:

    menopause thyroid cancer

    Buy_without prescription…

  64. IAN Says:

    boniva and bone thinning and breaks

    Buy_now it…

  65. REGINALD Says:

    free fast diet tips

    Buy_generic meds…

  66. WALLACE Says:


    Buy Viagra

    Check Quality Generic Pills Today!…