5
<title>FocusManager Node Plugin</title>
6
<link rel="stylesheet" href="http://yui.yahooapis.com/3.4.0pr3/build/cssgrids/grids-min.css">
7
<link rel="stylesheet" href="../assets/css/main.css">
8
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
9
<script src="../../build/yui/yui-min.js"></script>
14
<h1>FocusManager Node Plugin</h1>
17
<a href="#toc" class="jump">Jump to Table of Contents</a>
21
<div id="main" class="yui3-u">
22
<div class="content"><div class="intro">
24
When designing widgets that manage a set of descendant controls (i.e. buttons
25
in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to
26
limit the number of descendants in the browser's default tab flow. The fewer
27
number of descendants in the default tab flow, the easier it is for keyboard
28
users to navigate between widgets by pressing the tab key. When a widget has
29
focus it should provide a set of shortcut keys (typically the arrow keys)
30
to move focus among its descendants.
34
To this end, the Focus Manager Node Plugin makes it easy to define a Node's
35
focusable descendants, define which descendant should be in the default tab
36
flow, and define the keys that move focus among each descendant.
37
Additionally, as the CSS
38
<a href="http://www.w3.org/TR/CSS21/selector.html#x38"><code>:focus</code></a>
39
pseudo class is not supported on all elements in all browsers,
40
the Focus Manager Node Plugin provides an easy, cross-browser means of
45
<h2 id="getting-started">Getting Started</h2>
48
To include the source files for FocusManager Node Plugin and its dependencies, first load
49
the YUI seed file if you haven't already loaded it.
52
<pre class="code prettyprint"><script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script></pre>
56
Next, create a new YUI instance for your application and populate it with the
57
modules you need by specifying them as arguments to the <code>YUI().use()</code> method.
58
YUI will automatically load any dependencies required by the modules you
62
<pre class="code prettyprint">// Create a new YUI instance and populate it with the required modules.
63
YUI().use('node-focusmanager', function (Y) {
64
// FocusManager Node Plugin is available and ready for use. Add implementation
65
// code here.
70
For more information on creating YUI instances and on the
71
<a href="http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_use"><code>use()</code> method</a>, see the
72
documentation for the <a href="../yui/index.html">YUI Global object</a>.
76
<h2 id="using-the-focus-manager-node-plugin">Using the Focus Manager Node Plugin</h2>
79
To use the Focus Manager Node Plugin, start by identifying the parent element
80
of the elements whose focus is to be managed. For example, consider the
81
following toolbar HTML:
84
<pre class="code prettyprint"><div id="toolbar-1">
85
<input type="button" name="btn-add" value="Add">
86
<input type="button" name="btn-edit" value="Edit">
87
<input type="button" name="btn-print" value="Print">
88
<input type="button" name="btn-delete" value="Delete">
89
<input type="button" name="btn-open" value="Open">
90
<input type="button" name="btn-save" value="Save">
91
</div></pre>
95
To manage the focus of each button in the toolbar, start by retrieving a
96
<a href="../node">Node</a> instance representing the toolbar's root element
97
(<code><div id="toolbar-1"></code>).
99
<a href="http://yuilibrary.com/yui/docs/api/classes/Node.html#method_plug"><code>plug</code></a>
100
method, passing in a reference to the Focus Manager Node Plugin
101
(<code>Y.Plugin.NodeFocusManager</code>) as the first argument, followed by an
102
object literal of configuration attributes as the second.
105
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_descendants"><code>descendants</code></a>
106
configuration attribute to specify the child nodes of the root node whose
107
focus is to be managed. The
108
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_descendants"><code>descendants</code></a>
109
configuration attribute accepts a string representing a CSS selector, making it
110
very easy to identify the elements whose focus should be managed. For the
111
toolbar, a value of "input" will be passed as the value of the
112
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_descendants"><code>descendants</code></a>
113
configuration attribute.
117
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_keys"><code>keys</code></a>
118
configuration attribute to identify which keys move focus between each of the
119
specified descendant Nodes. The
120
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_keys"><code>keys</code></a>
121
configuration attribute accepts an object literal, the format of which is
122
<code>{ next: "down:40", previous: "down:38" }</code>. The value for the
123
<code>next</code> and <code>previous</code> sub attributes are used to attach
124
<a href="../event/index.html#keylistener"><code>key</code></a> event listeners. Each
125
value represents the type of event
126
("down" for <code>mousedown</code>, "up" for <code>mouseup</code>, and
127
"press" for <code>keypress</code>) and key codes ("40" for the down arrow key)
128
to use to navigate to the next/previous descendant. For the
130
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_keys"><code>keys</code></a>
131
configuration attribute will be set to a value of
132
<code>{ next: "down:39", previous: "down:37" }</code>, enabling the user to
133
move focus among each button using the left and right arrows keys.
135
the <a href="../event/key.html">Using the key Event</a> section of
136
the Event documentation for more information on "key" event listeners.)
139
<pre class="code prettyprint">YUI().use("node-focusmanager", function(Y) {
141
// Retrieve the Node instance representing the toolbar
142
// (<div id="toolbar">) and call the "plug" method
143
// passing in a reference to the Focus Manager Node Plugin.
145
var toolbar = Y.one("#toolbar-1");
147
toolbar.plug(Y.Plugin.NodeFocusManager, {
149
// CSS Selector indicating the descendant Nodes whose focus to manage
151
descendants: "input",
153
// Move focus the buttons in the toolbar by pressing the left and
154
// right arrow keys
156
keys: { next: "down:39", previous: "down:37" }
165
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_circular"><code>circular</code></a>
166
configuration attribute to indicate that focus should be directed to the first
167
or last descendant when the user has navigated to the first or last descendant.
170
<pre class="code prettyprint">YUI().use("node-focusmanager", function(Y) {
172
// Retrieve the Node instance representing the toolbar
173
// (<div id="toolbar">) and call the "plug" method
174
// passing in a reference to the Focus Manager Node Plugin.
176
var toolbar = Y.one("#toolbar-1");
178
toolbar.plug(Y.Plugin.NodeFocusManager, {
180
// CSS Selector indicating the descendant Nodes whose focus to manage
182
descendants: "input",
184
// Move focus the buttons in the toolbar by pressing the left and
185
// right arrow keys
187
keys: { next: "down:39", previous: "down:37" },
196
<h3 id="activedescendant-attribute">The <code>activeDescendant</code> Attribute</h3>
199
The <a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_activeDescendant"><code>activeDescendant</code></a>
200
attribute represents which of the Focus Manager's
201
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_descendants"><code>descendants</code></a> is either
202
currently focused or is focusable (<code>tabIndex</code> attribute is set to 0).
203
As the user moves focus among the Focus Manager's defined descendants, the <a href="../api/plugin.NodeFocusManager.html#config_activeDescendant"><code>activeDescendant</code></a>
204
configuration attribute is updated to remain in sync with the currently
209
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_activeDescendant"><code>activeDescendant</code></a>
210
configuration attribute can be set two different ways: via markup or via
212
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_activeDescendant"><code>activeDescendant</code></a>
213
configuration attribute via markup, simply set the <code>tabIndex</code>
214
attribute to 0 for the element that should be considered the active descendant.
215
If the <code>tabIndex</code> attribute isn't set on any of the descendants
216
the active descendant will be set to 0, or the index of the first
217
enabled descendant. The following example shows how to make the second button
218
in the toolbar the active descendant.
221
<pre class="code prettyprint"><div id="toolbar-1">
222
<input type="button" name="btn-add" value="Add">
223
<input type="button" tabindex="0" name="btn-edit" value="Edit">
224
<input type="button" name="btn-print" value="Print">
225
<input type="button" name="btn-delete" value="Delete">
226
<input type="button" name="btn-open" value="Open">
227
<input type="button" name="btn-save" value="Save">
228
</div></pre>
233
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_activeDescendant"><code>activeDescendant</code></a>
234
configuration attribute can also be set via the object literal of configuration
235
attributes passed to the
236
<a href="http://yuilibrary.com/yui/docs/api/classes/Node.html#method_plug"><code>plug</code></a>
240
<pre class="code prettyprint">YUI().use("node-focusmanager", function(Y) {
242
// Retrieve the Node instance representing the toolbar
243
// (<div id="toolbar">) and call the "plug" method
244
// passing in a reference to the Focus Manager Node Plugin.
246
var toolbar = Y.one("#toolbar-1");
248
toolbar.plug(Y.Plugin.NodeFocusManager, {
250
// CSS Selector indicating the descendant Nodes whose focus to manage
252
descendants: "input",
254
// Move focus the buttons in the toolbar by pressing the left and
255
// right arrow keys
257
keys: { next: "down:39", previous: "down:37" },
259
// Make the second button in the toolbar the active descendant
268
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_activeDescendant"><code>activeDescendant</code></a>
269
configuration attribute can also be set at runtime via the
270
<a href="http://yuilibrary.com/yui/docs/api/classes/Attribute.html#method_set"><code>set</code></a>
274
<pre class="code prettyprint">YUI().use("node-focusmanager", function(Y) {
276
// Retrieve the Node instance representing the toolbar
277
// (<div id="toolbar">) and call the "plug" method
278
// passing in a reference to the Focus Manager Node Plugin.
280
var toolbar = Y.one("#toolbar-1");
282
toolbar.plug(Y.Plugin.NodeFocusManager, {
284
// CSS Selector indicating the descendant Nodes whose focus to manage
286
descendants: "input",
288
// Move focus the buttons in the toolbar by pressing the left and
289
// right arrow keys
291
keys: { next: "down:39", previous: "down:37" }
295
// Make the second button in the toolbar the active descendant
296
toolbar.focusManager.set("activeDescendant", 1);
301
<h3 id="styling-focus">Styling Focus</h3>
304
One of the challenges to styling the focus state of HTML elements is that
306
<a href="http://www.w3.org/TR/CSS21/selector.html#x38"><code>:focus</code></a>
307
pseudo class is limited to the <code>a</code> element in Internet Explorer.
308
To fix this problem, the Focus Manager Node Plugin provides a
309
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_focusClass"><code>focusClass</code></a>
310
configuration attribute that makes it easy to style focus across <em>all</em>
311
elements in <em>all</em> browsers</a>.
315
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_focusClass"><code>focusClass</code></a>
316
configuration attribute can be used one of two ways. The first way is to
317
simply pass a string representing the class name to be applied to the currently
318
focused descendant Node instance. For example, to apply a class of "focus" to
319
each button in the toolbar, set the the
320
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_focusClass"><code>focusClass</code></a>
321
configuration attribute to a value of "focus":
324
<pre class="code prettyprint">YUI().use("node-focusmanager", function(Y) {
326
// Retrieve the Node instance representing the toolbar
327
// (<div id="toolbar">) and call the "plug" method
328
// passing in a reference to the Focus Manager Node Plugin.
330
var toolbar = Y.one("#toolbar-1");
332
toolbar.plug(Y.Plugin.NodeFocusManager, {
334
// CSS Selector indicating the descendant Nodes whose focus to manage
336
descendants: "input",
338
// Move focus the buttons in the toolbar by pressing the left and
339
// right arrow keys
341
keys: { next: "down:39", previous: "down:37" },
343
focusClass: "focus"
351
Often styling focusable elements such as <code><input></code>s
352
requires wrapping them in decorator elements, since <code><input></code>
353
elements cannot have children. In such cases, it is likely the class name used
354
to style focus would be applied to the element decorating the focused
355
descendant, rather than the descendant itself. For this reason, it is also
356
possible to pass an object literal as the value of the
357
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_focusClass"><code>focusClass</code></a>
358
configuration attribute that defines not only the class name to be used to
359
indicate focus, but a function used to retrieve the Node instance to which
360
the class name should be applied. For example, if each button in the toolbar
361
where decorated by a <code><span></code>, the "focus" class could be
362
applied to the parent <code><span></code> of the focused
363
<code><input></code> using the following code:
366
<pre class="code prettyprint">YUI().use("node-focusmanager", function(Y) {
368
// Retrieve the Node instance representing the toolbar
369
// (<div id="toolbar">) and call the "plug" method
370
// passing in a reference to the Focus Manager Node Plugin.
372
var toolbar = Y.one("#toolbar-1");
374
toolbar.plug(Y.Plugin.NodeFocusManager, {
376
// CSS Selector indicating the descendant Nodes whose focus to manage
378
descendants: "input",
380
// Move focus the buttons in the toolbar by pressing the left and
381
// right arrow keys
383
keys: { next: "down:39", previous: "down:37" },
386
className: "focus", // The class name to use
387
fn: function (node) {
388
// The Node instance to which the class should be applied
389
return node.get("parentNode");
399
As demonstrated in the example, the function passed as the value of the
400
<code>fn</code> property is passed a reference to the currently focused
401
descendant. That Node reference is then used to return the Node to which the
402
class name is to be applied.
405
<h3 id="managing-focus">Managing Focus</h3>
408
The Focus Manager Node Plugin manages focus among its defined descendants as an
409
atomic operation: the Focus Manager's
410
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_focused"><code>focused</code></a>
411
configuration attribute is set to
412
<code>true</code> the first time any descendant is focused, and is set to
413
<code>false</code> the first time no descendant is focused. The
414
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#config_focused"><code>focused</code></a>
415
configuration attribute is read only, and is set either via user interaction
416
(the user focuses one of the defined descendant elements using either the
417
keyboard or the mouse), or programmatically via the
418
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#method_focus"><code>focus</code></a>
420
<a href="http://yuilibrary.com/yui/docs/api/classes/plugin.NodeFocusManager.html#method_blur"><code>blur</code></a>
421
methods, as illustrated in the following example:
424
<pre class="code prettyprint">YUI().use("node-focusmanager", function(Y) {
426
// Retrieve the Node instance representing the toolbar
427
// (<div id="toolbar">) and call the "plug" method
428
// passing in a reference to the Focus Manager Node Plugin.
430
var toolbar = Y.one("#toolbar-1");
432
toolbar.plug(Y.Plugin.NodeFocusManager, {
434
// CSS Selector indicating the descendant Nodes whose focus to manage
436
descendants: "input",
438
// Move focus the buttons in the toolbar by pressing the left and
439
// right arrow keys
441
keys: { next: "down:39", previous: "down:37" }
446
// Listen for when the "focused" attribute changes
448
toolbar.focusManager.after("focusedChange", function (event) {
451
Y.log("The toolbar has focus");
454
Y.log("The toolbar has lost focus");
459
// Focus the current active descendant, setting the "focused" attribute to true
461
toolbar.focusManager.focus();
463
// Focus the second descendant in the toolbar, making it the active descendant
464
// (this won't change the "focused" attribute, meaning the "focusedChange"
465
// event handler won't be called.)
467
toolbar.focusManager.focus(1);
470
// Blur the current active descendant, setting the "focused" attribute to false
471
// and causing the "focusedChange" event handler to be called.
473
toolbar.focusManager.blur();
478
<h3 id="best-practices">Best Practices</h3>
481
While it is possible to use the Focus Manager Node Plugin to manage focus
482
among descendants of any type, it is recommended to use it with elements that
483
are natively in the the browser's default tab flow. Doing so provides two primary
484
benefits: The first is that your code will work in all popular browsers, since some browsers don't support
486
<a href="http://www.w3.org/TR/html401/interact/forms.html#adef-tabindex" title="Forms in HTML documents"><code>tabIndex</code></a>
487
attribute on all elements. Sticking with the elements that natively support <a href="http://www.w3.org/TR/html401/interact/forms.html#adef-tabindex" title="Forms in HTML documents"><code>tabIndex</code></a>
488
as defined in the HTML 4.01 specification will ensure better cross-browser
492
<p>The second benefit is that by using the set of natively
493
focusable HTML elements, users of screen readers will still perceive the
494
Focus Manager's defined descendants as actionable/clickable elements.</p>
498
<div id="sidebar" class="yui3-u">
500
<div id="toc" class="sidebox">
502
<h2 class="no-toc">Table of Contents</h2>
508
<a href="#getting-started">Getting Started</a>
511
<a href="#using-the-focus-manager-node-plugin">Using the Focus Manager Node Plugin</a>
514
<a href="#activedescendant-attribute">The <code>activeDescendant</code> Attribute</a>
517
<a href="#styling-focus">Styling Focus</a>
520
<a href="#managing-focus">Managing Focus</a>
523
<a href="#best-practices">Best Practices</a>
533
<div class="sidebox">
535
<h2 class="no-toc">Examples</h2>
539
<ul class="examples">
542
<li data-description="Creating an accessible toolbar using the Focus Manager Node Plugin and Node's support for the WAI-ARIA Roles and States.">
543
<a href="node-focusmanager-1.html">Accessible Toolbar</a>
548
<li data-description="Creating an accessible menu button using the Focus Manager Node Plugin, Event's delegation support and mouseenter event, along with the Overlay widget and Node's support for the WAI-ARIA Roles and States.">
549
<a href="node-focusmanager-3.html">Accessible Menu Button</a>
563
<script src="../assets/vendor/prettify/prettify-min.js"></script>
564
<script>prettyPrint();</script>