node-focusmanager.js revision 22c19eb81c822db2d3e97baa28081f1660ec84b3
* <p>The Focus Manager Node Plugin makes it easy to manage focus among * a Node's descendants. Primarily intended to help with widget development, * the Focus Manager Node Plugin can be used to improve the keyboard * accessibility of widgets.</p> * 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 * pseudo class is not supported on all elements in all * the Focus Manager Node Plugin provides an easy, cross-browser means of * @module node-focusmanager // Frequently used strings // Collection of keys that, when pressed, cause the browser viewport * The NodeFocusManager class is a plugin for a Node instance. The class is used * via the <a href="Node.html#method_plug"><code>plug</code></a> method of Node * and should not be instantiated directly. * @class NodeFocusManager * Boolean indicating that one of the descendants is focused. * String representing the CSS selector used to define the descendant Nodes * whose focus should be managed. * <p>Node, or index of the Node, representing the descendant that is either * focused or is focusable (<code>tabIndex</code> attribute is set to 0). * The value cannot represent a disabled descendant Node. Use a value of -1 * to remove all descendant Nodes from the default tab flow. * If no value is specified, the active descendant will be inferred using * the following criteria:</p> * <li>Examining the <code>tabIndex</code> attribute of each descendant and * using the first descendant whose <code>tabIndex</code> attribute is set * <li>If no default can be inferred then the value is set to either 0 or * the index of the first enabled descendant.</li> * @attribute activeDescendant // The user passed a reference to a Node that wasn't one // Setting the "activeDescendant" attribute to the index // of a disabled descendant is invalid. * Object literal representing the keys to be used to navigate between the * next/previous descendant. The format for the attribute's value is * <code>{ next: "down:40", previous: "down:38" }</code>. The value for the * "next" and "previous" properties are used to attach * <a href="event/#keylistener"><code>key</code></a> event listeners. See * the <a href="event/#keylistener">Using the key Event</a> section of * the Event documentation for more information on "key" event listeners. * String representing the name of class applied to the focused active * descendant Node. Can also be an object literal used to define both the * class name, and the Node to which the class should be applied. If using * an object literal, the format is: * <code>{ className: "focus", fn: myFunction }</code>. The function * referenced by the <code>fn</code> property in the object literal will be * passed a reference to the currently focused active descendant Node. * Boolean indicating if focus should be set to the first/last descendant * when the end or beginning of the descendants has been reached. // Boolean indicating if the NodeFocusManager is active. // NodeList representing the descendants selected via the // "descendants" attribute. // Object literal mapping the IDs of each descendant to its index in the // "_descendants" NodeList. // Reference to the Node instance to which the focused class (defined // by the "focusClass" attribute) is currently applied. // Number representing the index of the last descendant Node. // Array of handles for event handlers used for a NodeFocusManager instance. * @method _initDescendants * @description Sets the <code>tabIndex</code> attribute of all of the * descendants to -1, except the active descendant, whose * <code>tabIndex</code> attribute is set to 0. // If the user didn't specify a value for the // "activeDescendant" attribute try to infer it from // Need to pass "2" when using "getAttribute" for IE to get // the attribute value as it is set in the markup. // Need to use "parseInt" because IE always returns the // value as a number, whereas all other browsers return // the attribute as a string when accessed // If the user didn't specify a value for the // "activeDescendant" attribute and no default value could be // determined from the markup, then default to 0. // Check to make sure the active descendant isn't disabled, // and fall back to the first enabled descendant if it is. // Need to set the "tabIndex" attribute here, since the // "activeDescendantChange" event handler used to manage // the setting of the "tabIndex" attribute isn't wired up yet. * @description Determines if the specified Node instance is a descendant * managed by the Focus Manager. * @param node {Node} Node instance to be checked. * @return {Boolean} Boolean indicating if the specified Node instance is a * descendant managed by the Focus Manager. * @method _removeFocusClass * @description Removes the class name representing focus (as specified by * the "focusClass" attribute) from the Node instance to which it is * @method _detachKeyHandler * @description Detaches the "key" event handlers used to support the "keys" * @description Prevents the viewport from scolling when the user presses * the up, down, left, or right key. * @description Fires the click event if the enter key is pressed while * focused on an HTML element that is not natively clickable. Y.
log((
"Firing click event for node:" +
oTarget.
get(
"id")),
"info",
"nodeFocusManager");
* @method _attachKeyHandler * @description Attaches the "key" event handlers used to support the "keys" // In Opera it is necessary to call the "preventDefault" method in // response to the user pressing the arrow keys in order to prevent // the viewport from scrolling when the user is moving focus among // the focusable descendants. // For all browsers except Opera: HTML elements that are not natively // focusable but made focusable via the tabIndex attribute don't // fire a click event when the user presses the enter key. It is // possible to work around this problem by simplying dispatching a // click event in response to the user pressing the enter key. * @method _detachEventHandlers * @description Detaches all event handlers used by the Focus Manager. * @method _detachEventHandlers * @description Attaches all event handlers used by the Focus Manager. Y.
log(
"Attaching base set of event handlers.",
"info",
"nodeFocusManager");
// For performance: defer attaching all key-related event // handlers until the first time one of the specified // descendants receives focus. Y.
log(
"Attaching key event handlers.",
"info",
"nodeFocusManager");
// Detach this "focusedChange" handler so that the // key-related handlers only get attached once. // Protected event handlers * @method _onDocMouseDown * @description "mousedown" event handler for the owner document of the * @param event {Object} Object representing the DOM event. // Check to make sure that the target isn't a child node of one // of the focusable descendants. // The target was a non-focusable descendant of the root // node, so the "focused" attribute should be set to false. // Fix general problem in Webkit: mousing down on a button or an // anchor element doesn't focus it. // For all browsers: makes sure that the descendant that // was the target of the mousedown event is now considered the // Document doesn't receive focus in Webkit when the user mouses // down on it, so the "focused" attribute won't get set to the // The goal is to force a blur if the user moused down on // either: 1) A descendant node, but not one that managed by // the FocusManager, or 2) an element outside of the * @description "focus" event handler for the owner document of the * @param event {Object} Object representing the DOM event. // The target is a descendant of the root Node. // The user has focused a focusable descendant. // The user has focused a child of the root Node that is // not one of the descendants managed by this Focus Manager // so clear the currently focused descendant. // The target is some other node in the document. * @description Keydown event handler that moves focus to the next * @param event {Object} Object representing the DOM event. * @param activeDescendant {Number} Number representing the index of the * next descendant to be focused * @description Keydown event handler that moves focus to the previous * @param event {Object} Object representing the DOM event. * @param activeDescendant {Number} Number representing the index of the * next descendant to be focused. * @method _afterActiveDescendantChange * @description afterChange event handler for the * "activeDescendant" attribute. * @param event {Object} Object representing the change event. * @description Focuses the active descendant and sets the * <code>focused</code> attribute to true. * @param index {Number} Optional. Number representing the index of the * descendant to be set as the active descendant. * @param index {Node} Optional. Node instance representing the * descendant to be set as the active descendant. // In Opera focusing a <BUTTON> element programmatically // will result in the document-level focus event handler // "_onDocFocus" being called, resulting in the handler // incorrectly setting the "focused" Attribute to false. To fix // this, set a flag ("_focusTarget") that the "_onDocFocus" method // can look for to properly handle this edge case. * @description Blurs the current active descendant and sets the * <code>focused</code> attribute to false. // For Opera and Webkit: Blurring an element in either browser // doesn't result in another element (such as the document) // being focused. Therefore, the "_onDocFocus" method // responsible for managing the application and removal of the // focus indicator class name is never called. * @description Enables the Focus Manager. * @description Disables the Focus Manager by detaching all event handlers. * @description Refreshes the Focus Manager's descendants by re-executing the * CSS selector query specified by the <code>descendants</code> attribute.