Improving Accessibility Through Focus Management

By Todd KlootsFebruary 23rd, 2009

A core requirement for developers using ARIA is to provide keyboard access for widgets, as users of screen readers rely on the keyboard to navigate web sites and applications. A large part of providing keyboard access is managing focus of a widget’s descendants (e.g., buttons in a toolbar, tabs in a tablist, menuitems in a menu, etc.), and the W3C guidelines provide two different approaches for doing so. This article explains both approaches and provides some practical advice for choosing between them.

The Benefit of Focus Management

As outlined in the ARIA specification and corresponding Best Practices document, providing keyboard access requires, in part, that each widget has one tab stop by default and is responsible for discreetly managing focus for its descendants. Following these guidelines enables users to quickly navigate a page or application by using the tab key to move between widgets. Once a user has tabbed into a widget, they can then use other keys (the arrow keys for example) to move focus amongst the widget’s descendants.

Two Approaches

When it comes to managing focus, the WAI-ARIA Best Practices document provides two different techniques for developers: the Roaming TabIndex Technique and use of the activedescendant property.

Using the Roaming TabIndex Technique

The Roaming TabIndex Technique requires each of a widget’s descendants be focusable, either through the use of natively focusable HTML elements, or by making an element focusable using the tabIndex attribute. To use this technique, begin by setting the tabIndex attribute of a widget’s first descendant to a value that is equal to or greater than 0. (A value of 0 will result in the tab order of the widget being relative to its position in the page. Use a value greater than 0 to precisely control a widget’s tab order.) Next, set the tabIndex attribute of all remaining descendants to -1. (A value of -1 removes an element from the default tab flow, while preserving its ability to be focused via JavaScript.) This ensures that all of a widget’s descendants are focusable via JavaScript, but only one is in the default tab flow of its containing page or application, and therefore focusable by the user.

With these tabIndex values, use a keydown event handler to manage focus of the descendants once the widget is focused by the user. As the user presses the arrow keys, call the focus method to activate the appropriate descendant and update the tabIndex of the remaining descendants so that the currently focused element is the only one that is natively focusable.

The following menu button example illustrates how to use the Roaming TabIndex Technique to create a widget that is both keyboard and screen-reader accessible. To test this example yourself, you can use the free NVDA Screen Reader and Firefox 3. Alternatively, you can watch the screen cast of the example running in Firefox 3 using the NVDA screen reader.

Roaming TabIndex Example

Test Menu Roaming TabIndex Example

Roaming TabIndex Example Screen Cast


Menu Button Using Roaming TabIndex Technique @ Yahoo! Video

The Roaming TabIndex Technique Best Practices

Having studied the WAI-ARIA Best Practices document, as well as having used the Roaming TabIndex Technique in several widget implementations, I have distilled several best practices for using this approach:

  • I prefer using natively focusable elements instead of using the tabIndex attribute to make non-focusable HTML elements focusable, for better backward compatibility in browsers that don’t support the tabIndex attribute on all elements.
  • Use the keydown event when binding handlers used to manage focus since it is not possible to handle non-alphanumeric keys using the keypress event in IE.
  • In most cases it is necessary to prevent the browser’s default behavior when handling key events.
  • When setting the tabIndex attribute, avoid using the setAttribute method, to prevent case-sensitivity mistakes in IE. Setting the tabIndex attribute using the camel-case DOM property is both the most compact and most compatible syntax across browsers. For example: myElement.tabIndex = -1;
  • When updating the tabIndex attribute of a widget’s descendants, set the attribute’s value to 0 before calling the focus method to ensure that the element’s default focus outline is rendered in IE.
  • When styling a descendant’s focused state, work with or augment the browser’s default rendering of focus rather than suppress it. The default rendering of focus is familiar to the user and, in many cases, consistent with the browser’s host OS. Working with the default focus model improves the learnability of the widget. If you suppress the browser’s default rendering of focus, be sure to provide a focus model that is consistent across your entire site or application so that the user only has to recognize and learn one focus model within a single context.
  • Set focus to HTML elements using both the setTimeout method and a try/catch block. Using setTimeout can help screen readers keep up while the focus is being changed. I have found this to be true when testing on VoiceOver for the Mac. A try/catch block can help suppress unwanted XUL-related errors logged to the console when focusing elements in Firefox.

Using the activedescendant Property

Unlike the Roaming TabIndex Technique, use of the activedescendant property doesn’t require any of a widget’s descendants to be focusable. Instead, this technique requires only that the widget’s root element be made focusable via the tabIndex attribute. (Note: this technique doesn’t work in the current version of Safari as it doesn’t support the tabIndex attribute on all HTML elements.) Using this approach, the activedescendant property is applied to the widget’s root element, and as the user presses the arrow keys, the value of the property is set to the id of the element that represents the user’s current selection. Since this approach doesn’t rely on focusing a widget’s descendants via the focus method, the browser will not provide any default rendering of the user’s current selection. Therefore, when using the activedescendant property the developer is responsible for rendering focus for the widget’s currently active descendant via CSS.

activedescendant Example

The following example adapts the previous example to illustrate how to use the activedescendant property.

Test activedescendant Example

activedescendant Example Screen Cast


Menu Button Using The "activedescendant" Property @ Yahoo! Video

As illustrated in the screen cast, the activedescendant property can provide a user experience that is identical to the Roaming TabIndex technique.

Best Practices for Using the activedescendant Property

  • Depending on the browser and the attribute’s value, setting the tabIndex attribute on a widget’s root element can result in a focus outline being drawn around the widget. For widgets with descendants, having a focus outline surround an entire control is both unfamiliar to the user (not something you’ll see on the desktop), as well as ugly. So when using the activedescendant property, it is best to suppress the focus outline. This can be accomplished by setting the outline CSS property in Firefox and IE 8, and using the hideFocus attribute for IE 6 and 7. Unfortunately it is not possible to suppress the focus outline in the current version of Opera.
  • Use CSS to render focus for a widget’s descendants in a way that is consistent with, and/or builds on, the default browser focus, or is consistent within the scope of the site or application.

Choosing a Technique

Having evaluated both the Roaming TabIndex Technique and the use of the activedescendant property, the Roaming TabIndex Technique is the better choice, because it is a solution that works “with the grain”. As such, it is more forward and backward compatible — especially when you are trying to support a wide array of browsers like we are at Yahoo!. Using the activedescendant property requires more effort for less overall benefit and compatibility. Here are the downsides to using the activedescendant property:

  • Requires browser support of the tabIndex attribute on all elements. Currently this not supported in Safari.
  • Suppressing the default focus outline drawn around a widget’s containing element is a slight pain in that it requires different techniques for different browsers. It turns into a bigger pain in Opera, where it is not only currently impossible but also exacerbated by Opera’s egregious focus model, illustrated in the following screen capture: Screen capture of the focus menu from the second example running in Opera 9.6
  • Loss of the default, familiar focus outline drawn around a widget’s descendants. The focus outline can be restored via CSS. However, developers get the focus outline for free when using the Roaming TabIndex Technique.
  • Since descendants aren’t focusable they cannot be clicked by pressing the enter key or space bar. This requires that developers listen explicitly for these key events and route the code responsible for handling the click event accordingly. When using the Roaming TabIndex technique, developers can simply listen for the click event.
  • Every descendant needs a unique id.

Unlike the activedescendant property, the Roaming TabIndex Technique allows widgets to be both keyboard accessible and screen-reader accessible in browsers that don’t support ARIA and don’t support the tabIndex attribute on all elements. For example, if a widget’s descendants are built using the set of natively focusable HTML elements, users of screen readers will still perceive them as actionable/clickable elements. Consider the following screen cast of our first example running in IE 7 (a browser without ARIA support) using JAWS 10.


Screen reader accessible Menu Button @ Yahoo! Video

In this example, while the user doesn’t perceive the button’s menu as a menu, the screen reader does announce each button in the menu as it is focused — letting the user know that each item is actionable/clickable. Additionally, since the button’s menu is built using the natively focusable <button> element, this widget will be keyboard accessible to all A-Grade browsers, not just those that support the tabIndex attribute on all elements.

I suspect that the activedescendant property was developed as an alternative to the Roaming TabIndex Technique in part because the focus and blur events don’t bubble like other DOM events. This was a problem since developers need to listen for these events in order to customize how focus is drawn in a way that works cross browser, and attaching individual focus and blur event handlers to each of a widget’s focusable descendants has consequences for performance — especially for large composite widgets like trees and menus. That said, since we now have an easy way of listening for focus and blur in a performance-conscious way, I feel like there are currently more downsides than upsides to using the activedescendant property.

11 Comments

  1. It’s funny, I posted working library as a suggestion for YUI3 three days ago – http://yuilibrary.com/projects/yui3/ticket/2526008

    http://code.google.com/p/qfocuser/ – standalone class for keyboard navigable AJAX widgets

  2. Yep, I strongly recommend roaming tabindex too. Nice post.

  3. John Snyders said:
    August 7, 2009 at 11:05 am

    This article was very helpful. I have one point of general confusion. Clearly being keyboard accessible is good for both non mouse users and sight impaired users. The examples often point to having a one tab stop widget and then using arrow keys. However my testing so far with JAWS 10 shows that the browser never receives the arrow keys because JAWS treats them as special reading keys. So now my widget is no longer keyboard accessible.
    Anyone have thoughts on this catch-22?

  4. Hi John -

    You are correct by default the virtual buffer is enabled in JAWS, meaning that the arrow keys are intercepted by the screen reader. This is the case for all screen readers. However, in the more recent versions of ARIA-enabled screen readers, the screen reader will automatically toggle the virtual buffer off by default when focus is given to an element with an ARIA role applied. And the last time I checked this was the behavior for JAWS 10 as well. If you are finding that not to be the case, perhaps you have your installation of JAWS configured differently.

    - Todd

  5. Victor Tsaran said:
    August 10, 2009 at 9:36 am

    @Todd: actually, the behavior you are describing comes from NVDA, http://www.nvda-project.org, and not JAWS, http://www.freedomscientific.com, screen reader. At least I do not remember seeing JAWS toggle virtual buffer off when an ARIA-enabled widget receives focus. NVDA, however, does so for sure.

    @David: the only way to force a screen reader to toggle virtual buffer is to place your widget inside a role of application. This is what we have done inside the “search assist” widget of the Yahoo! Search, http://www.ysearch.com.

  6. [...] some great examples of how widgets should react to keyboard use, and Todd Kloots of Yahoo does a great job of explaining the techniques behind good keyboard usability (also as a video and using YUI3 and focusing on WAI-ARIA). Patrick Lauke of Opera also wrote a [...]

  7. [...] Improving Accessibility Through Focus Management [...]

  8. [...] by using JavaScript tricks, you can make any element keyboard accessible. And if you build widgets, go even further by following the rules of keyboard navigation. You could [...]

  9. [...] by using JavaScript tricks, you can make any element keyboard accessible. And if you build widgets, go even further by following the rules of keyboard navigation. You could [...]

  10. [...] by using JavaScript tricks, you can make any element keyboard accessible. And if you build widgets, go even further by following the rules of keyboard navigation. You could [...]

  11. I must admit that I have been quite lax in making sure my sites are accessablet to keyboard only users. I should probably do something about this.