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.
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
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
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.
Ginader's Original Accessible Tabs Example
- all of the links in the tab menu are linked to the same anchor in a single
h2preceding 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
clickevent action is programmatically prevented (using
e.preventDefault()), and focus is set to the anchor within the
h2's text is dynamically replaced with the relevant tab menu link text
- because the anchor in the
tabindex="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
h2anchor, 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
h2anchor as you would expect it to
- interestingly, with NVDA 2009.1 in IE8, upon receiving focus the main
h2anchor is announced as both a link and a heading. In FF3.6, NVDA reads it as a heading only
Accessible Tabs Test Cases
- effectively the same as Ginader's Accessible Tabs, except that,
- the main
h2's anchor has
tabindex="0"to keep it programmatically focussable but out of the TAB order, since the
aelement is not a link and is invisible anyway
- same as Case #1, except that,
- upon clicking a tab menu link, focus is programmatically set to the
h2's anchor only after the
spanis written to the new current tab menu link, as opposed to before the
spanis 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
- same as Case #2, except that,
- each link in the tab menu targets an anchor within the relevant
h2in the previously hidden tab
div, and not an anchor in the same single
h2preceding all tab content, so there is no dynamic rewriting of an anchor within a single
- surprisingly, JAWS 9, 10, and 11 don't seem to have a problem following focus in IE8 or FF3.6
- same as Case #3, except that,
- each link in the tab menu targets the relevant
h2in the previously hidden tab
div, and not an anchor within that
- 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
idon the desired element seems cleaner and preferable to the use of an additional, non-linked
aelement as internal page anchor; also, this version requires no dynamic rewriting of a single
h2to 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?