Making actions keyboard accessible by using keyboard event handlers with WAI-ARIA controls
Contents
Status
- New technique.
- Add this as a related technique to F42 and F59, since this technique is a way to avoid those failures.
Applicability
HTML, Client-Side Scripting, and WAI-ARIA
WCAG references
This technique relates to:
- Success Criterion 2.1.1 (Keyboard)
- How to Meet 2.1.1 (Keyboard)
- Understanding Success Criterion 2.1.1 (Keyboard)
- Success Criterion 2.1.3 (Keyboard (No Exception))
- How to Meet 2.1.3 (Keyboard (No Exception))
- Understanding Success Criterion 2.1.3 (Keyboard (No Exception))
User Agent and Assistive Technology Support Notes
Description
The objective of this technique is to demonstrate how to add keyboard support to WAI-ARIA controls by invoking a scripted function in a way that is keyboard accessible. The keyboard handler must be triggered by a control that is itself keyboard accessible, either because it is a natively actionable HTML element or because the tabindex property has been used to make the control focusable. The keyboard event handlers of these elements are device independent.
The simplest design is to make the interactive control itself the focusable element, and to attach the keyboard event handlers to that element. However, the eventhandler model for the DOM is rich, and it is possible to provide one keyboard event handler that handles events from multiple different controls, if it is coded correctly.
In HTML, keyboard handlers may be triggered by keydown, keyup, or keypress events. It is also helpful to provide onclick handlers, which trigger the default action for the widget. For simple widgets, keyboard support may just provide support for the Enter key; such widgets invoke the default action both for onclick and for pressing Enter. However, many WAI-ARIA controls are complex and support a variety of keyboard commands for interacting with the widget. Recommended design patterns for keyboard interaction are defined in WAI-ARIA 1.0 Authoring Practices.
Examples
Example 1: Using Space to activate a button
This example is based on Example 1 from SCR35: Making actions keyboard accessible by using the onclick
event of anchors and buttons. Adding the ARIA role "button" makes it clear that this is a button control and not a link. Because we are using an anchor
element, the control is focusable and the browser will automatically call the onclick
handler for the Enter keystroke. Since users expect to be able to activate buttons with either Enter or Space, a keyboard handler provides the support for activating the button via the Space character.
Note that supporting SPACE in addition to ENTER is not required by WCAG 2.0; this control would still be keyboard accessible without the key event handler for SPACE. However, supporting platform keyboard conventions for controls is strongly encouraged.
Example Code:
<script> function doStuff() { //do stuff return false; } function keyHandler(event) { switch (event.which) { case KEY_SPACE: { event.stopPropagation; return doStuff(); break; } } //end switch return true; } </script> <a role="button" href="#" onclick="return doStuff();" onkeypress="return keyHandler(e);"> do stuff </a>
Example 2: Keyboard support for a set of toggle buttons
This example is derived from Open Ajax Alliance Example 3 - Button role example using text-only buttons, which contains complete source code and a live example. The complete example includes mouse as well as keyboard support.
ul
list mark-up is used to define the set of buttons. Each button is implemented by a li
element with role "button". The "aria-pressed" attribute indicates that these are toggle buttons. To make the buttons focusable, they are given "tabindex=0" attributes. An onkeydown
keyhandler has been attached to each button to toggle the button when Space or Enter is pressed.
Example Code:
<script> var KEY_ENTER = 13; var KEY_SPACE = 32; function buttonKey(event) { var ariaControls = '#' + $(this).attr('aria-controls'); switch (event.which) { case KEY_ENTER: case KEY_SPACE: { switch($(this).attr('aria-labelledby')) { case 'italic_label': { $(ariaControls).toggleClass('italic'); break; } case 'bold_label': { $(ariaControls).toggleClass('bold'); break; } case 'larger_label': { increaseFontSize(ariaControls); break; } case 'smaller_label': { decreaseFontSize(ariaControls); break; } } // end switch if ($(this).hasClass('toggleButton') == true) { // This is a toggle button: toggle aria-pressed state togglePressed(this); } else { // This is a momentary pushbutton: Set aria-pressed to true $(this).attr('aria-pressed', 'true'); } event.stopPropagation(); return false; } // end case } // end switch return true; }); // end button keydown handler // Attach the handler to the buttons var button = document.getElementById("larger1"); button.onkeydown = buttonKey; var button = document.getElementById("smaller1"); button.onkeydown = buttonKey; var button = document.getElementById("italic1"); button.onkeydown = buttonKey; var button = document.getElementById("bold1"); button.onkeydown = buttonKey; </script> <ul id="buttonset" class="buttons" title="Text Formatting Controls"> <li id="larger1" role="button" tabindex="0" aria-pressed="false" aria-controls="text1" aria-labelledby="larger_label">+</li> <li id="smaller1" role="button" tabindex="0" aria-pressed="false" aria-controls="text1" aria-labelledby="smaller_label" onkeydown="return buttonKey(event);">-</li> <li id="italic1" role="button" class="toggleButton italic" tabindex="0" aria-pressed="true" aria-controls="text1" aria-labelledby="italic_label" onkeydown="return buttonKey(event);">i</li> <li id="bold1" role="button" class="toggleButton" tabindex="0" aria-pressed="false" aria-controls="text1" aria-labelledby="bold_label" onkeydown="return buttonKey(event);">B</li> </ul>
Example 3: Attaching the keyboard handler to the set of toggle buttons
This example is very similar to Example 2. However, instead of attaching the onkeydown
handler to each button, we attach it to the ul
that encloses the set of buttons. In the handler, this
refers to the element that has focus. Since the buttons still take keyboard focus, this
identifies the button that should be toggled.
Example Code:
<script> ... // Attach the handler to the buttons, // replacing this section of Example 2 var button = document.getElementById("buttonset"); button.onkeydown = buttonKey; </script>
Resources
- WAI-ARIA 1.0 Authoring Practices, Keyboard and Structural Navigation
- WAI-ARIA 1.0 Authoring Practices, Common Widget Design Patterns
- Open Ajax Alliance Accessibility Examples
- HTML 4.01 Scripts
- Document Object Model (DOM) Technical Reports
Related Techniques
- G90: Providing keyboard-triggered event handlers
- H91: Using HTML form controls and links
- SCR20: Using both keyboard and other device-specific functions
- F42: Failure of Success Criterion 1.3.1 and 2.1.1 due to using scripting events to emulate links in a way that is not programmatically determinable
- F59: Failure of Success Criterion 4.1.2 due to using script to make div or span a user interface control in HTML
Tests
Procedure
For the interactive control:
- Check whether there are keyboard shortcuts for every operation of the interactive control.
- Check whether there are key handlers for keyboard shortcuts attached to the element or one of its ancestors.
- Check that there is at least one element between the control and the element with the appropriate key handler can take focus, that is,
- the element is a native HTML control, or
- the element has a tabindex with value of 0 or greater.
- Check that when focus is set to the focusable element in the ancestors of the interactive control, keyboard actions are handled by the key handler for the interactive control.
- Check that all key handlers execute the appropriate action for the interactive control.
Expected Results
- Checks 1, 2, 3, 4 and 5 are true.