1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright<div class='intro'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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 <a href='http://www.w3.org/TR/CSS21/selector.html#x38'>`:focus`</a> pseudo class is not supported on all elements in all browsers, the Focus Manager Node Plugin provides an easy, cross-browser means of styling focus.
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove{{>getting-started}}
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove<h2>Using the Focus Manager Node Plugin</h2>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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:
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright<div id='toolbar'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-add' value='Add'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-edit' value='Edit'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-print' value='Print'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-delete' value='Delete'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-open' value='Open'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-save' value='Save'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright To manage the focus of each button in the toolbar, start by retrieving a <a href='../node'>Node</a> instance representing the toolbar's root element (`<div id='toolbar'>`). Next, call the Node's <a href='{{apiDocs}}/classes/Node.html#method_plug'>`plug`</a> 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.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright Use the <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_descendants'>`descendants`</a> 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.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright Use the <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_keys'>`keys`</a> 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 `keys` 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 <a href='../event/key.html'>Using the key Event</a> section of the Event documentation for more information on 'key' event listeners.)
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove// Retrieve the Node instance representing the toolbar
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright// (<div id='toolbar'>) and call the 'plug' method
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove// passing in a reference to the Focus Manager Node Plugin.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrightvar toolbar = Y.one('#toolbar');
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove // CSS Selector indicating the descendant Nodes whose focus to manage
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright descendants: 'input',
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove // Move focus the buttons in the toolbar by pressing the left and
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove // right arrow keys
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright next: 'down:39',
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright previous: 'down:37'
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright Use the <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_circular'>`circular`</a> 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.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrightvar toolbar = Y.one('#toolbar');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright descendants: 'input',
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright keys: { next: 'down:39', previous: 'down:37' },
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove circular: true
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright<h3 id='activedescendant-attribute'>The `activeDescendant` Attribute</h3>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright The <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_activeDescendant'>`activeDescendant`</a> attribute represents which of the Focus Manager's <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_descendants'>`descendants`</a> 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.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright<div id='toolbar'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-add' value='Add'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' tabindex='0' name='btn-edit' value='Edit'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-print' value='Print'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-delete' value='Delete'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-open' value='Open'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright <input type='button' name='btn-save' value='Save'>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright The `activeDescendant` configuration attribute can also be set via the object literal of configuration attributes passed to the <a href='{{apiDocs}}/classes/Node.html#method_plug'>`plug`</a> method.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrightvar toolbar = Y.one('#toolbar');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright descendants: 'input',
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright keys: { next: 'down:39', previous: 'down:37' },
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove // Make the second button in the toolbar the active descendant
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove activeDescendant: 1
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright The `activeDescendant` configuration attribute can also be set at runtime via the <a href='{{apiDocs}}/classes/Attribute.html#method_set'>`set`</a> method.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrightvar toolbar = Y.one('#toolbar');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright descendants: 'input',
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright keys: { next: 'down:39', previous: 'down:37' }
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrighttoolbar.focusManager.set('activeDescendant', 1);
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright<h3 id='styling-focus'>Styling Focus</h3>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright One of the challenges to styling the focus state of HTML elements is that support for the CSS <a href='http://www.w3.org/TR/CSS21/selector.html#x38'>`:focus`</a> pseudo class is limited to the `a` element in Internet Explorer. To fix this problem, the Focus Manager Node Plugin provides a <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_focusClass'>`focusClass`</a> configuration attribute that makes it easy to style focus across <em>all</em> elements in <em>all</em> browsers</a>.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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':
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrightvar toolbar = Y.one('#toolbar');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright descendants: 'input',
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright keys: { next: 'down:39', previous: 'down:37' },
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright focusClass: 'focus'
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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 <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_focusClass'>`focusClass`</a> 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:
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrightvar toolbar = Y.one('#toolbar');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright descendants: 'input',
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright keys: { next: 'down:39', previous: 'down:37' },
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove focusClass: {
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright className: 'focus', // The class name to use
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove fn: function (node) {
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove // The Node instance to which the class should be applied
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright return node.get('parentNode');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright<h3 id='managing-focus'>Managing Focus</h3>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright The Focus Manager Node Plugin manages focus among its defined descendants as an atomic operation: the Focus Manager's <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#attr_focused'>`focused`</a> 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 <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#method_focus'>`focus`</a> and <a href='{{apiDocs}}/classes/plugin.NodeFocusManager.html#method_blur'>`blur`</a> methods, as illustrated in the following example:
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrightvar toolbar = Y.one('#toolbar');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright descendants: 'input',
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright keys: { next: 'down:39', previous: 'down:37' }
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright// Listen for when the 'focused' attribute changes
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathrighttoolbar.focusManager.after('focusedChange', function (event) {
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright Y.log('The toolbar has focus');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright Y.log('The toolbar has lost focus');
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright// Focus the current active descendant, setting the 'focused' attribute to true
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove// Focus the second descendant in the toolbar, making it the active descendant
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright// (this won't change the 'focused' attribute, meaning the 'focusedChange'
4e95ab13203fb7559acec02713acf9e41e1444faRyan Grove// event handler won't be called.)
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright// Blur the current active descendant, setting the 'focused' attribute to false
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright// and causing the 'focusedChange' event handler to be called.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright<h3 id='best-practices'>Best Practices</h3>
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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. Doing so provides two primary benefits: The first is that your code will work in all popular browsers, since some browsers don't support the <a href='http://www.w3.org/TR/html401/interact/forms.html#adef-tabindex' title='Forms in HTML documents'>`tabIndex`</a> attribute on all elements. Sticking with the elements that natively support `tabIndex` as defined in the HTML 4.01 specification will ensure better cross-browser keyboard support.
619812e02ae23b4b1b19c8dd18316f7666dc29e5Derek Gathright 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.