Background
Dirk Ginader's Accessible Tabs jQuery plugin is just awesome. I love it. Providing straightforward, accessible solutions like this to such commonly used, and very often inaccessible, web interfaces is the way to go. The key to the plugin, as Ginader clearly explains, is ensuring that navigation takes place when one clicks on a link in the tab list, that is, that focus actually moves to heading for the previously hidden selected tab.
Ginader's Approach
However, it seems that various screen readers have problems setting focus to elements that were recently hidden using display:none
. Dirk's clever solution is to dynamically rewrite and set focus to a single heading anchor positioned offscreen and preceding all of the tabs, instead of setting focus to the selected tab's own heading. Still, the navigation is taking place, and this is the key.
Another thoughtful feature of Ginader's Accessible Tabs plugin is that the currently selected tab list link gets prepended with a span
containing the useful text "current tab:". It is hidden offscreen, but is read by screen readers and so helps identify which tab is currently selected.
An Intriguing Problem
I was intrigued that the plugin, as noted in some of the comments on Dirk's blog, doesn't seem to work entirely as expected with JAWS 9 or 10 when used with Internet Explorer: focus unfortunately remains with the selected link in the tab list. This does not happen with JAWS 11, which follows focus properly. The JAWS 9 or 10 user is thus forced to arrow down through the tab list links to the relevant tab content; or, if using the "Say All" command, JAWS continues reading from the selected link in the tab list, eventually getting to the newly visible tab content. Interestingly, using Firefox, JAWS 9 and 10 work as you would hope, following focus to the tab content's main heading anchor.
A Possible Solution
As such, I did I some fooling around. Working through the simple test cases below, I think I found a potential solution to the problem when using JAWS in IE, and possibly even an improvement related to the tabindex
that is set on the heading anchor.
In short, and as far as I am able to tell, the solution has something to do with the various events that will cause JAWS to update its virtual buffer, which include a change in an element's class
attribute or its innerText
or innerHTML
property. If, during a tab link's click
event, focus is programmatically set to the main tab heading before the "current tab:" span
is written to the selected tab link, JAWS will not properly follow focus. This seems to happen because the virtual buffer gets updated after the focus has already been set. If, on the other hand, focus is set after the span
is written to the tab list link, it would appear that JAWS has already updated its virtual buffer and can now happily follow focus. In any case, and even if my understanding of what is happening is not correct, the effect of modifying the point at which focus is set to the heading anchor seems to work. See Case #2 for the working example.
About the Test Cases
While the text cases linked to below are not jQuery plugins, they do use jQuery that effectively reproduces the sequence of actions and events implemented in Dirk's plugin. I do profess to not being a jQuery or JavaScript expert, so it may be that I'm missing something. Still, testing these various implementations of an accessible tabs interface revealed some hopeful, and sometimes interesting results, even highlighting some issues with the current JAWS, as well as NVDA screen readers. At the very least, they serve to highlight how much variation there is in the ways different screen readers and browsers manage focus.
Thanks go to Dirk, and to those that worked on it with him, for pursuing and developing a really great plugin.
If anyone gets different results from those I describe, or gets results using other screen readers, or has any comments at all, please send me a note.
Update (June 03, 2010): the features in Case #4 have been incorporated in version 1.7 of Ginader's Accessible Tabs plugin, and can also be found in the official github repository.
Ginader's Original Accessible Tabs Example
Ginader's Accessible Tabs
- all of the links in the tab menu are linked to the same anchor in a single
h2
preceding all of the tabs' content to deal with some screen readers' difficulty in setting focus to a recently hidden element made visible - the tab menu links' default
click
event action is programmatically prevented (usinge.preventDefault()
), and focus is set to the anchor within theh2
- the
h2
's text is dynamically replaced with the relevant tab menu link text - because the anchor in the
h2
hastabindex="0"
, it shows up in the TAB order, but because it is hidden offscreen, it doesn't display visually when navigating by keyboard, but adds an extra press of the TAB key to navigate back up from the selected tab's content to the last link in the tab menu - with JAWS 11 and NVDA 2009.1, upon clicking a tab menu link, focus properly moves to the main
h2
anchor, in both IE8 and FF3.6 - with JAWS 9 and 10 in IE8, however, focus stays with the tab menu link
- oddly, in FF3.6 with JAWS 9 and 10, focus moves to the
h2
anchor as you would expect it to - interestingly, with NVDA 2009.1 in IE8, upon receiving focus the main
h2
anchor is announced as both a link and a heading. In FF3.6, NVDA reads it as a heading only
Accessible Tabs Test Cases
Case #1
- effectively the same as Ginader's Accessible Tabs, except that,
- the main
h2
's anchor hastabindex="-1"
instead oftabindex="0"
to keep it programmatically focussable but out of the TAB order, since thea
element is not a link and is invisible anyway - JavaScript file: accTabs1.js
Case #2
- same as Case #1, except that,
- upon clicking a tab menu link, focus is programmatically set to the
h2
's anchor only after thespan
is written to the new current tab menu link, as opposed to before thespan
is written, as it is in Case #1 and in Ginader's Accessible Tabs - this version seems to solve the focus problem experienced with JAWS 9 and 10 in IE
- JavaScript file: accTabs2.js
Case #3
- same as Case #2, except that,
- each link in the tab menu targets an anchor within the relevant
h2
in the previously hidden tabdiv
, and not an anchor in the same singleh2
preceding all tab content, so there is no dynamic rewriting of an anchor within a singleh2
required - surprisingly, JAWS 9, 10, and 11 don't seem to have a problem following focus in IE8 or FF3.6
- JavaScript file: accTabs3.js
Case #4
- same as Case #3, except that,
- each link in the tab menu targets the relevant
h2
in the previously hidden tabdiv
, and not an anchor within thath2
- JAWS 9, 10, and 11 don't seem to have a problem following focus in IE8 or FF3.6 with this version either
- this would be my preferred version with respect to markup since linking to an
id
on the desired element seems cleaner and preferable to the use of an additional, non-linkeda
element as internal page anchor; also, this version requires no dynamic rewriting of a singleh2
to serve as the heading for every tab - of course,
tabindex="-1"
is even less valid on a heading element (though it is completely valid in HTML5); but again, what's a minor invalid attribute if the user experience and accessibility is improved and no otherwise ill effects are caused? - JavaScript file: accTabs4.js