Is your rich JavaScript app keyboard-friendly?
reworded & improved 10.12.2012
CSP notes 07.05.2014
Keyboard support for onclick
statements on HTML tags
onclick keyboard accessibility - demo, test case & compatibility table
Question
So, you have a lot of code like this:
<span onclick="foo()"> content </span>
(or you dynamically attach event listeners).
Will I be able to use your page if I don't have a mouse? Or if I prefer keyboard?
TL;DR
For maximum portability, please use the following instead:
<a tabindex="0" href="javascript:void(0);" onclick="foo()"> content </a>
Ideally, you should use a button
, but this is probably impractical.
Introduction
Is your interactive site friendly for keyboard users?
If you have lots of onclick
events bound in many places in your code to execute some JavaScript, under certain
circumstances keyboard users will be able to work with it just fine, but it's not always the case, and it depends heavily
on the browser. This page is supposed to be a review of pros and cons of various attitudes.
Why keyboard support matters
- For people with Repetitive strain injury, it's much better to use the keyboard than the mouse whenever possible.
- People with various kinds of disabilities and elderly people, who can't very precisely navigate the mouse, might find keyboard more suitable to their needs.
- It's much faster for the power users to use the keyboard.
- Some laptop users prefer not to use neither external mouse nor touchpad.
- Mouse/touchpad might be unavailable / malfunctioning etc.
- For the reasons above, operability of interfaces through the keyboard is a part of WCAG 2.0 guidelines.
Disclaimer
- All tests under Windows XP. Let me know if there are some cross-platform discrepancies.
- If you know some workarounds or other problems not mentioned here, I'm looking for your feedback!
Usage
All of the HTML tags below in the leftmost column have:
tabindex="0"
to make them Tab-navigable from keyboardonclick : "alert('hi')"
(added viaaddEventListener
/attachEvent
)
Tab-navigate through the items. Click Enter when each of
the items has focus, to check whether onclick
will be fired when you activate the item from keyboard.
- GREEN= onclick event fired from keyboard, without side-effects.
- RED= onclick event NOT fired from keyboard, only from mouse.
- OTHER= onclick fired from keyboard, but with side-effects (either when launched from keyboard or mouse).
- See also: onclick + JAWS
Example | IE8 | IE7/IE9/IE10 | Firefox3.6...Fx18 | Opera 11.6...12.0 | Chrome18...25 | Safari 5.1.7 |
<a tabindex="0" href="javascript:void(0);"> | onbeforeunload issues [6] |
opens blank new page on mouse middle click [1] | not focusable via by default! [4] |
|||
<a tabindex="0" href="#"> | resets the current URL hash [2]; opens a copy of the page on middle click [7] |
|||||
<a>any of above WITHOUT tabindex | not focusable via TAB [5] | |||||
<a>WITHOUT href. | focus can be lost after [3] | not fired | not fired | not fired | not fired | |
<span> / <div> / <p> | focus can be lost after [3] | not fired | not fired | not fired | not fired | |
[1] Neither
e.preventDefault() nor return false
(inside onclick , onmousedown , onmouseup ) change anything;the only way I've found to disable the native event of opening a link in the new tab is to have some event listener attached to onmousedown invoke alert() (but 1] this works only in Firefox, and
2] is far from being user-friendly, so it should not be used).However, since the newly opened page is blank, it's not a really big deal.
[2] Resetting the hash has also a side-effect of scrolling to the top. To prevent this,
one can use
#nonExistingAnchor in a link instead of just # ,
and then no scrolling would take place. A sensible convention can be to use
#void as this anchor (and of course do not create
any object on the page with that id). Furthermore, in modern versions of
Firefox, Chrome and Opera, you can do a following hack (taking advantage of History API)
to restore the old hash:
<a href="#void" onclick="alert('hi');">click me</a> <script> window.onhashchange = function(e){ var newUrl = e.newURL, newUrlLen = newUrl.length; if(newUrl.substring(newUrlLen-5,newUrlLen)=="#void"){ history.replaceState(null,null,e.oldURL); } } </script>
[3] Clicking ENTER when focus is on any non-form and
non-link HTML element (like
div , span etc.) -- even if that element
doesn't have any event listeners attached -- astonishingly moves
the focus to the closest <button> in the markup (!).
Not the case if clicking the element by mouse. Likely an IE8 bug.
[4] User can opt in via Preferences > Advanced > "Press
Tab to highlight each item on a webpage".
The default behavior is ridiculous, but power users who want keyboard accessibility
certainly will have the option enabled.
[5] TAB-navigation in Opera
is separated from navigation over the links.
TAB-navigation cycles only over the elements that have explicitly set tabindex e.g. to 0 (and by default,
over the form elements). It doesn't cycle
over the links that do not have tabindex .To cycle over the links, there are separate keyboard shortcuts CTRL-UP and CTRL-DOWN. The good side is that the two cooperate together -- can be used alternately; i.e. using (SHIFT+)TAB or CTRL-UP/DOWN moves the focus to the closest element of the currently focused one. Still, if one wants to force all links to be accessible also by TAB, the tabindex="0"
can be added to them to achieve this.By the way, remember not to override this shortcut (CTRL-UP/DOWN) if you're going to enable rich keyboard shortcuts in your application, as you can do more harm than good to the users of Opera who are accustomed to this browser shortcuts.
[6]
If:
Since you're using onbeforeunload you probably need to do that, so take a look
at your listeners and add return false at the end of the handlers.
[7]
In Chrome, opens a new tab only when current URL's hash is different than link href's hash.
|
Edit (05.2014): Content Security Policy issues (CSP)
One of the drawbacks of the javascript:void(0)
attitude in the context of modern web
development is that it's incompatible with Content Security Policy, which when enabled in the response
headers of your HTML files tells the browser to disallow inline scripts (as a means to improve websites
security, particularly to mitigate XSS risk).
When following javascript:void(0)
advice with CSP enabled, you may see lots of warnings
in your browser's console. If you don't want that warnings, you may instead follow advice in
note 2 of this article.
Corollaries
To provide keyboard-navigability of your control on the page:
- Do not attach
onclick
event listeners todiv
s,span
s etc. - If you attach
onclick
event listeners toa
, remember to set alsohref
. Otherwise, the event couldn't be fired from the keyboard. - Ideally, you should use
input
s /button
s, however this comes with the styling issues and the inherent "pushability" of them. The added value on the other hand is that buttons can be activated with SPACE, not only ENTER. - The most sensible tradeoff is to use
<a href='javascript:void(0);'>
and usereturn false
in the listeners. Then the only problem is opening new page on mouse middle click (which can be mitigated if really needed via some additional JavaScript). - Opera 12 stands out positively, and handles
onclick
from keyboard on almost all the HTML tags on which it makes sense. It would be much easier if other vendors implemented that behavior, but since it's not the case, the WCAG recommendations, which state that input, buttons and anchors have to be used to provide keyboard accessibility, should be followed.