Return to ARIA Examples Index, or the ARIA Accessibility Home Page

ARIA Tab Panel Example

Features

List of product features....

Notes:

Although the code is short and simple, it is fully accessible. This example uses:

  1. aria-selected to show what tab is selected
  2. aria-controls to show the relationship between tabs and tab panels
  3. aria-hidden, to hide the unselected tab panel
  4. When an tab is selected the following states are toggled: aria-selected in both tab elements and aria-hidden on both tab panels.
  5. CSS selectors are used to bind aria-hidden and CSS display:none.
  6. CSS selectors are used to bind aria-selected and the classes for selected or non selected tab panels.
  7. Keyboard accessibility is given by adding the enter key to the basic click event in the jQuery code.
  8. The tabs can also toggle using the left and right arrow keys (as a jQuery function)
  9. Note that often only the active tab is in the tabbing order. However, in this example both tabs are always in the tabbing order. I felt this pattern worked better for this context.

 

Step by Step: How we made the tab panel (tutorial)

In the discussion on ARIA, we discussed 5 steps to making complex things accessible with ARIA.

  1. Alert users to what each elements is: Their role (such as checkbox).
  2. Alert users to their properties and important relationships (such as disabled, required,and other labels).
  3. Alert users to what each element is doing: The state (such as checked).
  4. Alert users to changes in their state.
  5. Make sure widgets are keyboard accessible and focus works predictably. Events can be triggered though the keyboard, and it should be intuitive to the user. All controls should receive focus via tabbing though the keyboard.

Step 1: Set roles.

Lots to do here. We have two tabs, and tabs need to be in a containing tab list. You can think of them as list items in a list.

<ul class="tablist" role="tablist">
<li id="tab1" class="tab" role="tab" ...>Prices</li>
<li id="tab2" class="tab" role="tab"
...>Features </li>
</ul>

We also have tab panels

<div id="panel1" class="panel"  role="tabpanel" .... >
<h3 ....>Prices</h3> 
List of prices</div>  

Note that the panel content uses headers, as per good accessible authoring practice. Any content in the panels needs to be accessible.

Step 2: Set properties and important relationships

We want to make sure the structure, relationships between elements and groups, properties and labels are all clear in the code. They can be clear from the standard HTML code, or using ARIA.

Firstly note the structure. The tabs are all rapped inside the tablist role so that they belong together is implied in the code or DOM. So that group is clear.

However the relationship between the tabs and the panel is not clear from the code, so we need to use aria.

With tab panels, the tabs really label the panels (they are a bit like headers) and the tabs control the panels. In other words clicking on the tabs controls what panel is visible. So now we have:

      
<ul class="tablist" role="tablist">         
<li id="tab1" class="tab" aria-controls="panel1" role="tab" ...>Prices</li>         
<li id="tab2" class="tab" aria-controls="panel2" role="tab" ..>Features </li>         
</ul>      
   
<div id="panel1" class="panel" aria-labelledby="tab1" role="tabpanel" ..>       
<h3 ..>Prices</h3>                   
 List of prices      
</div> 
     
<div id="panel2" class="panel" aria-labelledby="tab2" role="tabpanel" ..>        
<h3 ..>Features</h3>      
List of product features....          
</div>      
 

Note that it is always worth looking at any requirements such as required states, and supported states for your roles before deciding what properties and states you need.

Step 3: Set the initial states

A good rule of thumb is elements that change how they look often have states that can change. The tab change in that they go from looking selected to looking not selected. The panels change from being visible to non-visible. So we are going to use aria-hidden and aria-selected as our states.

We also use CSS selectors, to change the look of the tabs and show and hide the panels based on the aria-hidden value. Now I do not need to change their CSS class in the code, only the value of aria-hidden or aria-selected.

In the CSS:

li[aria-selected='true'] {
color: black;
background-color: #fff;
border-bottom: 1px solid white;
}

div[aria-hidden='true'] {
display: none;
}

and the updated HTML:

<ul class="tablist" role="tablist">
<li id="tab1" class="tab" aria-controls="panel1" aria-selected="true" role="tab"..>
Prices</li>
<li id="tab2" class="tab" aria-controls="panel2" role="tab" aria-selected="false" ..>
Features </li>
</ul>


<div id="panel1" class="panel" aria-labelledby="tab1" role="tabpanel" aria-hidden="false">
<h3 ..>Prices</h3>
List of prices
</div>

<div id="panel2" class="panel" aria-labelledby="tab2" role="tabpanel" aria-hidden="true">
<h3 ..>Features</h3>
List of product features....
</div>

 

A word about backward compatibility....

Unfortunately IE 9 does not support the CSS selectors above. (IE 10 does support them.) So to make this work with older browser I added a class hidden:

.hidden {display:none;}

I then added "hidden" to the class attribute in the HTML to hide the hidden items in older browsers.

class = "... hidden"

 

Step 4: Set changes in state.

We discussed above that we are using the aria states of aria-hidden and aria-selected, and we are using CSS selectors so that whenever aria-hidden='true' the panels will be hidden and when aria-hidden='false' the panels will be visible. The look of the tabs will likewise change based on the value of aria-selected.

In the code we have a function to change the values of aria-hidden and aria-selected whenever a tab is clicked.

$("li[role='tab']").click(function(){  
$("li[role='tab']").attr("aria-selected","false"); //deselect all the tabs
$(this).attr("aria-selected","true"); // select this tab
var tabpanid= $(this).attr("aria-controls"); //find out what tab panel this tab controls
var tabpan = $("#"+tabpanid);
$("div[role='tabpanel']").attr("aria-hidden","true"); //hide all the panels
tabpan.attr("aria-hidden","false"); // show our panel
});

For older browsers, whenever I set aria-hidden = "true" in the script, I also added the class hidden to the same selector:

$("....").addClass("hidden");

I also removed it when I set aria-hidden = "false":

$("...").removeClass("hidden");

Or, I set the class name to panel (and delete any other classes)

"...".className = "panel";

This makes the script much more robust in older browsers.

Step 4: Manage focus and keyboard accessibility.

Both the focus and keyboard accessibility needs to be managed. Firstly lets make sure the tabs and visible tab panel can be tabbed to from the keyboard. As our tabs are line items, they would not normally receive focus or be in the tab order. To change this we add tabindex = 0 to any element that should be able to receive focus. Now the completed HTML looks like:

<ul class="tablist" role="tablist">
<li id="tab1" class="tab" aria-controls="panel1" aria-selected="true" role="tab" tabindex="0">
Prices</li>
<li id="tab2" class="tab" aria-controls="panel2" role="tab" aria-selected="false" tabindex="0">
Features </li>
</ul>


<div id="panel1" class="panel" aria-labelledby="tab1" role="tabpanel" aria-hidden="false">
<h3 tabindex="0">Prices</h3>
List of prices
</div>

<div id="panel2" class="panel" aria-labelledby="tab2" role="tabpanel" aria-hidden="true">
<h3 tabindex="0">Features</h3>
List of product features....
</div>


Next let's deal with the keyboard accessibility and make sure our jQuery selectors are device independent. In the code we used the mouse event "click()" to trigger the button function: $("li[role='tab']").click(function()

We need to add events as well for people who can not use a mouse.

The following function gives keyboard accessibility for our tabs, so that pressing enter when a tab has focus (key-code 13) is the same as clicking the tab with a mouse.


$("li[role='tab']").keydown(function(ev) {
if (ev.which ==13) {
$(this).click()
}
});

For this design pattern we want the right and left arrow keys inside the tab list to switch the tabs, moving the focus and toggling the selection. Altogether the code for this keyboard functionality looks like this:

//This adds keyboard function that pressing an arrow left or arrow right from the tabs toggle the tabs.
$("li[role='tab']").keydown(function(ev) {
if ((ev.which ==39)||(ev.which ==37)) {
 var selected= $(this).attr("aria-selected");
 if (selected =="true"){
   $("li[aria-selected='false']").attr("aria-selected","true").focus() ;
   $(this).attr("aria-selected","false");
   var tabpanid= $("li[aria-selected='true']").attr("aria-controls");
   var tabpan = $("#"+tabpanid);
   $("div[role='tabpanel']").attr("aria-hidden","true");
   tabpan.attr("aria-hidden","false");

   }
}
});

Author: Lisa Seeman