Accessibility Examples
Example Start
Lunch Options
- Thai
- Subway
- Jimmy Johns
- Radio Maria
- Rainbow Gardens
Drink Options
- Water
- Tea
- Coffee
- Cola
- Ginger Ale
Example End
Example Description
Type: Best Practice
Simple example of a radio button widget using ARIA CSS selectors to display checked state. Background images are typically not displayed in high contrast mode. For this reason, background images should not be relied upon to display essential visual information.
Keyboard Support
- Tab: Move between button items and text area.
- Enter or space: Toggle aria-checked state of checkbox with keyboard focus.
Example Markup
- ARIA 1.0: [aria-checked]
- ARIA 1.0: [aria-labelledby]
- ARIA 1.0: [role="radio"]
- ARIA 1.0: [role="radiogroup"]
Browser Compatibility
- osx: Firefox 3.6 (C)
- osx: Opera 11.0 (C)
- osx: Safari 5.0 (C)
- win: Firefox 3.6 (C)
- win: Internet Explorer 8.0 (C)
- win: Opera 11.0 (C)
- win: Safari 5.0 (C)
HTML Source Code
<div role="application">
<h3 id="rg1_label">Lunch Options</h3>
<ul class="radiogroup"
id="rg1"
role="radiogroup"
aria-labelledby="rg1_label"
>
<li id="r1"
tabindex="-1"
role="radio"
aria-checked="false">
Thai
</li>
<li id="r2"
tabindex="-1"
role="radio"
aria-checked="false">
Subway
</li>
<li id="r3"
tabindex="-1"
role="radio"
aria-checked="false">
Jimmy Johns
</li>
<li id="r4"
tabindex="0"
role="radio"
aria-checked="true">
Radio Maria
</li>
<li id="r5"
tabindex="-1"
role="radio"
aria-checked="false">
Rainbow Gardens
</li>
</ul>
<!-- Start of second Radio Group -->
<h3 id="rg2_label">Drink Options</h3>
<ul id="rg2"
class="radiogroup"
role="radiogroup"
aria-labelledby="rg2_label"
>
<li id="r6"
tabindex="0"
role="radio"
aria-checked="false">
Water
</li>
<li id="r7"
tabindex="-1"
role="radio"
aria-checked="false">
Tea
</li>
<li id="r8"
tabindex="-1"
role="radio"
aria-checked="false">
Coffee
</li>
<li id="r9"
tabindex="-1"
role="radio"
aria-checked="false">
Cola
</li>
<li id="r10"
class="last"
tabindex="0"
role="radio"
aria-checked="false">
Ginger Ale
</li>
</ul>
</div>
CSS Code
ul.radiogroup {
margin: 0;
padding: 0;
}
ul.radiogroup li {
margin: 0;
margin-left: 1em;
padding: 4px;
padding-left: 20px;
list-style: none;
width: 6em;
}
ul.radiogroup li:hover {
padding: 2px 18px;
background-position: 2px 4px;
border: gray 2px solid;
}
ul.radiogroup li.selected {
padding: 2px 18px;
background-position: 2px 4px;
border: black 2px solid;
}
.offscreen {
position: absolute;
left: -200em;
top: -20em;
}
li[aria-checked='true'] {
background: url('http://www.oaa-accessibility.org/media/examples/images/radio-checked.gif') no-repeat 4px 6px;
}
li[aria-checked='false'] {
background: url('http://www.oaa-accessibility.org/media/examples/images/radio-unchecked.gif') no-repeat 4px 6px;
}
Javascript Source Code
$(document).ready(function() {
var group1 = new radioGroup('rg1');
var group2 = new radioGroup('rg2');
}); // end ready
function keyCodes () {
// Define values for keycodes
this.enter = 13;
this.space = 32;
this.left = 37;
this.up = 38;
this.right = 39;
this.down = 40;
}
//
// Function radioGroup() is a class to define an ARIA-enabled radiogroup widget.
//
// This widget attaches to an unordered list and makes each list entry a group
// of radio buttons.
//
// @param (id object) id is the html id of the <ul> to attach to
//
// @return N/A
//
function radioGroup(id) {
var thisObj = this;
///////// define widget properties ///////////////
this.$id = $('#' + id);
// find all list items with a role of radio
this.$buttons = this.$id.find('li').filter('[role=radio]');
// Store the currently checked item
this.$checked = this.$buttons.filter('[aria-checked=true]');
this.checkButton = true; // set to false during ctrl+arrow operations;
this.$active = null; // the selected button (may not be checked)
this.keys = new keyCodes();
///////////// Bind Event handlers ////////////////
this.$buttons.click(function(e) {
return thisObj.handleClick(e, $(this));
});
this.$buttons.keydown(function(e) {
return thisObj.handleKeyDown(e, $(this));
});
this.$buttons.keypress(function(e) {
return thisObj.handleKeyPress(e, $(this));
});
this.$buttons.focus(function(e) {
return thisObj.handleFocus(e, $(this));
});
this.$buttons.blur(function(e) {
return thisObj.handleBlur(e, $(this));
});
}
//
// Function selectButton() is a member function to select and possibly check a button in the
// radioGroup.
//
// @param ($id object) $id is the jQuery object of the button to select
//
// @return N/A
//
radioGroup.prototype.selectButton = function($id) {
if (this.checkButton == true) {
// checking the button
// set the previous button's aria-checked attribute to false
this.$checked.attr('aria-checked', 'false');
// set the new button's aria-checked attribute to true
$id.attr('aria-checked', 'true');
if (this.$checked.length == 0) { // no previously checked group buttons
// the first and last items in the group will have
// tabindex=0. Remove them both from the tab order.
this.$buttons.first().attr('tabindex', '-1');
this.$buttons.last().attr('tabindex', '-1');
}
else {
// remove the previously checked item from
// the tab order
this.$checked.attr('tabindex', '-1');
}
// Place this button in the tab order
$id.attr('tabindex', '0');
// update the stored $checked object
this.$checked = $id;
}
// reset the checkButton flag - in case it was false
this.checkButton = true;
// update the stored $active object
this.$active = $id;
// give this button the selected class
$id.addClass('selected');
} // end selectButton()
//
// Function handleKeyDown() is a member function to process keydown events for the radioGroup.
//
// @param (e object) e is the event object
//
// @param ($id object) $is is the jquery object of the triggering element
//
// @return (boolean) Returns false if consuming event; true if propagating
//
radioGroup.prototype.handleClick = function(e, $id) {
if (e.altKey || e.ctrlKey || e.shiftKey) {
// do nothing
return true;
}
// simply consume the event - browser calls focus
e.stopPropagation();
return false;
} // end handleClick()
//
// Function handleKeyDown() is a member function to process keydown events for the radioGroup.
//
// @param (e object) e is the event object
//
// @param ($id object) $is is the jquery object of the triggering element
//
// @return (boolean) Returns false if consuming event; true if propagating
//
radioGroup.prototype.handleKeyDown = function(e, $id) {
if (e.altKey) {
// do nothing
return true;
}
switch (e.keyCode) {
case this.keys.space:
case this.keys.enter: {
if (e.ctrlkey || e.shiftKey) {
// do nothing
return true;
}
// select and check the button
this.selectButton($id);
e.stopPropagation();
return false;
}
case this.keys.left:
case this.keys.up: {
var $prev = $id.prev(); // the previous button
if (e.shiftKey) {
// do nothing
return true;
}
// if this was the first item
// select the last one in the group.
if ($id.index() == 0) {
$prev = this.$buttons.last();
}
if (e.ctrlKey) {
// set checkButton to false so
// focus does not check button
this.checkButton = false;
}
// give the previous button focus
$prev[0].focus();
e.stopPropagation();
return false;
}
case this.keys.right:
case this.keys.down: {
var $next = $id.next(); // the next button
if (e.shiftKey) {
// do nothing
return true;
}
// if this was the last item,
// select the first one in the group.
if ($id.index() == this.$buttons.length - 1) {
$next = this.$buttons.first();
}
if (e.ctrlKey) {
// set checkButton to false so
// focus does not check button
this.checkButton = false;
}
// give the next button focus
$next[0].focus();
e.stopPropagation();
return false;
}
} // end switch
return true;
} // end handleKeyDown()
//
// Function handleKeyPress() is a member function to process keydown events for the radioGroup.
// This is needed to prevent browsers that process window events on keypress (such as Opera) from
// performing unwanted scrolling of the window, etc.
//
// @param (e object) e is the event object
//
// @param ($id object) $is is the jquery object of the triggering element
//
// @return (boolean) Returns false if consuming event; true if propagating
//
radioGroup.prototype.handleKeyPress = function(e, $id) {
if (e.altKey) {
// do nothing
return true;
}
switch (e.keyCode) {
case this.keys.space:
case this.keys.enter: {
if (e.ctrlKey || e.shiftKey) {
// do nothing
return true;
}
}
case this.keys.left:
case this.keys.up:
case this.keys.right:
case this.keys.down: {
if (e.shiftKey) {
// do nothing
return true;
}
e.stopPropagation();
return false;
}
} // end switch
return true;
} // end handleKeyPress()
//
// Function handleFocus() is a member function to process focus events for the radioGroup.
//
// @param (e object) e is the event object
//
// @param ($id object) $is is the jquery object of the triggering element
//
// @return (boolean) Returns false if consuming event; true if propagating
//
radioGroup.prototype.handleFocus = function(e, $id) {
// Do button selection processing
this.selectButton($id);
return true;
} // end handleFocus()
//
// Function handleBlur() is a member function to process blur events for the radioGroup.
//
// @param (e object) e is the event object
//
// @param ($id object) $is is the jquery object of the triggering element
//
// @return (boolean) Returns false if consuming event; true if propagating
//
radioGroup.prototype.handleBlur = function(e, $id) {
// remove the focus styling from this buttons
$id.removeClass('selected');
return true;
} // end handleBlur()