YUI 3: FocusManager Node Plugin [beta]

When designing widgets that manage a set of descendant controls (i.e. buttons in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to limit the number of descendants in the browser's default tab flow. The fewer number of descendants in the default tab flow, the easier it is for keyboard users to navigate between widgets by pressing the tab key. When a widget has focus it should provide a set of shortcut keys (typically the arrow keys) to move focus among its descendants.

To this end, the Focus Manager Node Plugin makes it easy to define a Node's focusable descendants, define which descendant should be in the default tab flow, and define the keys that move focus among each descendant. Additionally, as the CSS :focus pseudo class is not supported on all elements in all A-Grade browsers, the Focus Manager Node Plugin provides an easy, cross-browser means of styling focus.

Getting Started

Include Dependencies

The easiest way to include the source files for and its dependencies is to add the YUI seed file to your page, using the following script tag, and allow the YUI instance to download any additional files which may be required:

The YUI instance will automatically pull down 's source files and any missing dependencies when the node-focusmanager module is used. This helps you avoid having to manually manage the list of files needed on your page to support multiple components while also optimizing your initial page weight by loading files only when they are required.

If you do want to include file dependencies manually on your page, the YUI Dependency Configurator can be used to determine the list of files you need to include in order to use .

The YUI Instance

Once you have the YUI seed file on your page (yui-min.js), you can create a new YUI instance for your application to use and populate it with the modules you need, specified as the first set of arguments to the use method:

The last argument passed to use is a callback function. The callback function will be invoked as soon as the YUI instance is done downloading any required files missing from your page. Once those files are loaded, your local YUI instance will be supplemented with the classes which make up the node-focusmanager module and any modules it depends on. A reference to the populated YUI instance (Y) is passed back to your callback function. Within your callback, then, you can start writing your application code based on your own custom instance of YUI.

For more information on creating instances of YUI and the use method, see the YUI Global object documentation.

Using the Focus Manager Node Plugin

To use the Focus Manager Node Plugin, start by identifying the parent element of the elements whose focus is to be managed. For example, consider the following toolbar HTML:

To manage the focus of each button in the toolbar, start by retrieving a Node instance representing the toolbar's root element (<div id="toolbar-1">). Next, call the Node's plug method, passing in a reference to the Focus Manager Node Plugin (Y.Plugin.NodeFocusManager) as the first argument, followed by an object literal of configuration attributes as the second.

Use the descendants configuration attribute to specify the child nodes of the root node whose focus is to be managed. The descendants configuration attribute accepts a string representing a CSS selector, making it very easy to identify the elements whose focus should be managed. For the toolbar, a value of "input" will be passed as the value of the descendants configuration attribute.

Use the keys configuration attribute to identify which keys move focus between each of the specified descendant Nodes. The keys configuration attribute accepts an object literal, the format of which is { next: "down:40", previous: "down:38" }. The value for the next and previous sub attributes are used to attach key event listeners. Each value represents the type of event ("down" for mousedown, "up" for mouseup, and "press" for keypress) and key codes ("40" for the down arrow key) to use to navigate to the next/previous descendant. For the toolbar the keys configuration attribute will be set to a value of { next: "down:39", previous: "down:37" }, enabling the user to move focus among each button using the left and right arrows keys. (See the Using the key Event section of the Event documentation for more information on "key" event listeners.)

Use the circular configuration attribute to indicate that focus should be directed to the first or last descendant when the user has navigated to the first or last descendant.

The activeDescendant Attribute

The activeDescendant attribute represents which of the Focus Manager's descendants is either currently focused or is focusable (tabIndex attribute is set to 0). As the user moves focus among the Focus Manager's defined descendants, the activeDescendant configuration attribute is updated to remain in sync with the currently focused descendant.

The activeDescendant configuration attribute can be set two different ways: via markup or via script. To set the activeDescendant configuration attribute via markup, simply set the tabIndex attribute to 0 for the element that should be considered the active descendant. If the tabIndex attribute isn't set on any of the descendants the active descendant will be set to 0, or the index of the first enabled descendant. The following example shows how to make the second button in the toolbar the active descendant.

The activeDescendant configuration attribute can also be set via the object literal of configuration attributes passed to the plug method.

The activeDescendant configuration attribute can also be set at runtime via the set method.

Styling Focus

One of the challenges to styling the focus state of HTML elements is that support for the CSS :focus pseudo class is limited to the a element in Internet Explorer. To fix this problem, the Focus Manager Node Plugin provides a focusClass configuration attribute that makes it easy to style focus across all elements in all A-Grade browsers.

The focusClass configuration attribute can be used one of two ways. The first way is to simply pass a string representing the class name to be applied to the currently focused descendant Node instance. For example, to apply a class of "focus" to each button in the toolbar, set the the focusClass configuration attribute to a value of "focus":

Often styling focusable elements such as <input>s requires wrapping them in decorator elements, since <input> elements cannot have children. In such cases, it is likely the class name used to style focus would be applied to the element decorating the focused descendant, rather than the descendant itself. For this reason, it is also possible to pass an object literal as the value of the focusClass configuration attribute that defines not only the class name to be used to indicate focus, but a function used to retrieve the Node instance to which the class name should be applied. For example, if each button in the toolbar where decorated by a <span>, the "focus" class could be applied to the parent <span> of the focused <input> using the following code:

As demonstrated in the example, the function passed as the value of the fn property is passed a reference to the currently focused descendant. That Node reference is then used to return the Node to which the class name is to be applied.

Managing Focus

The Focus Manager Node Plugin manages focus among its defined descendants as an atomic operation: the Focus Manager's focused configuration attribute is set to true the first time any descendant is focused, and is set to false the first time no descendant is focused. The focused configuration attribute is read only, and is set either via user interaction (the user focuses one of the defined descendant elements using either the keyboard or the mouse), or programmatically via the focus and blur methods, as illustrated in the following example:

Best Practices

While it is possible to use the Focus Manager Node Plugin to manage focus among descendants of any type, it is recommended to use it with elements that are natively in the the browser's default tab flow. In other words, use it with elements that natively support the tabIndex as defined in the HTML 4.01 specification. Doing so provides two primary benefits: The first is that your code will work in all of the A-Grade browsers browsers. Unlike the other A-Grade browsers Safari 3 doesn't currently support the tabIndex attribute on all elements. Therefore, sticking with the elements that natively support the tabIndex as defined in the HTML 4.01 specification will ensure better cross-browser keyboard support. The second benefit is that by using the set of natively focusable HTML elements users of screen readers will still perceive the Focus Manager's defined descendants as actionable/clickable elements.

Support & Community

Forums & Blog

YUI 3 discussion forums are hosted on YUILibrary.com.

In addition, please visit the YUIBlog for updates and articles about the YUI Library written by the library's developers.

Filing Bugs & Feature Requests

The YUI Library's public bug tracking and feature request repositories are located on the YUILibrary.com site. Before filing new feature requests or bug reports, please review our reporting guidelines.

Copyright © 2009 Yahoo! Inc. All rights reserved. Copyright | Privacy Policy | Terms of Use | Job Openings