Accessible Buttons Jquery Plugin
Please note: The following should be considered an experiment. There is rarely a good reason not to use a <button>
element for button-like controls, especially if it means loading a javascript resource just to polfill the behaviors that <button>
already offers.
How many of the elements that users can click on in your web pages and applications are actually <button>
s?
As outlined in Marco Zehe's post, Making Clickables Accessible, it is possible, with the help of attribution specified in WAI-ARIA, to make interactively functional, clearly defined buttons using just about any element. This is especially useful in situations when an extra element (a <button>
) would otherwise have to be shoe-horned into your slim, semantic markup.
There's no reason at all in javascript-click-binding-expand-event-thingy terms, but managing accessibility is not so trivial. For a start, assistive technologies (AT) won't be aware of the element's role as a button unless we make it explicit. Neither do some elements — unlike buttons and links — receive focus without deliberate intervention. Even if they were focusable, they're not rigged to the key events (ENTER or SPACE) that are necessary to press the button via the keyboard. Balls!
To collectively solve these problems, I've created a tiny (just under 1K minified) jQuery plugin called buttonlike.js. The plugin …
- Defines elements as buttons with
role="button"
- Gives them
tabindex="0"
so they are focusable - While focused, triggers the faux buttons' click events on ENTER and SPACE keydown events
- Allows you to optionally define relationships between the "button" and the element(s) it affects using
aria-controls
- Allows you to optionally make the button a "toggle" using
aria-pressed
(true / false)
Example 1:
Buttonlike.js is perhaps at its most helpful just used without any of the options, applied to any elements in the page that have role="button"
already. It's an easy way to ensure these "buttons" are screenreader and keyboard accessible.
$('[role="button"]').buttonlike();
Example 2:
Here is an example using a button-like h3#toggle
that toggles the state of a div#summary
:
$('h3').buttonlike({controls: 'summary', pressed: false});
The markup
<h3>
<span role="button" aria-controls="summary" tabindex="0" aria-pressed="false">Heading</span>
</h3>
<div id="summary">
...content...
</div>
Notes: Because we wish the heading level to be announced, we must preserve the heading "as is" and attach our button functionality to a child <span>
, which is dynamically injected. Why not inject a true <button>
in this case? Just because a span is much more malleable in terms of styling. In most cases, a <span>
will simply inherit the styles of the heading. You can then build on this baseline.
In addition, the option of pressed is set to "false". This means aria-pressed
is used, with an initial value of "false". This value toggles between true and false according to click events triggered directly or via the keyboard.
Example 3:
This button affects multiple elements simultaneously. The pressed option is not applicable, so it defaults to "null" and the aria-pressed
attribute is not used:
$('button#delete').buttonlike({controls: 'summary body'});
Notes: In this example, we've made a button "button-like"! This is fine, if you are using a <button>
anyway but want to take advantage of the plugin's other features. Instead of role="button"
, the <button>
will be given type="button"
. This is important because it prevents some user agents from considering the button a submit button when within a form. Also, note the space separated IDs for the controls option. A space separated list is perfectly legal for aria-controls
.
<button id="delete" type="button" aria-controls="summary body">Delete</button>
<div id="summary">
...content...
</div>
<div id="body">
...content...
</div>
Download
Perhaps rarely, buttonlike.js isn't a jQuery plugin that lets you do anything amazing with the DOM. What it does instead is help you make the many amazing things that you might choose to do a bit more sensical to AT users. If that's the sort of thing you think's a good idea, include the tiny script before all your click(function()
stuff in the document. Download below and please suggest any improvements (or point out any errors) in the comments.
Important Note: As Steve "Smart Arse" Faulkner correctly points out in the comments, placing the button role on semantically specific elements breaks with The Second Rule Of Aria Use. I have changed the plugin accordingly: For headings and other elements that must retain their semantic status for AT, a child span element is inserted and the button functionality is placed on the <span>
instead. Also, certain "grouping" elements (eg. <ul>
and <tr>
) will refuse to become "button-like". An error note is written to the console. Cheers.