diff -r 322d0feea350 -r 89ef5ed3c48b src/cm/media/js/lib/yui/yui_3.10.3/docs/node-focusmanager/node-focusmanager-button.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui_3.10.3/docs/node-focusmanager/node-focusmanager-button.html Tue Jul 16 14:29:46 2013 +0200 @@ -0,0 +1,843 @@ + + + + + Example: Accessible Menu Button + + + + + + + + + + +
+
+

+
+ + Jump to Table of Contents + + +

Example: Accessible Menu Button

+
+
+
+
+

+ This example illustrates how to use 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 to + create an accessible menu button. +

+
+ +
+ + + + +
+ Move To + +
+
(Accessible Menu Button results here)
+ + + +
+ +

Setting Up the HTML

+ +

+For a menu button, start with an <a> element whose +href attribute is set to the id of an <div> +that wraps a list of <input> elements. +Therefore, without JavaScript and CSS, the menu button is simply an in-page +link to a set of additional buttons. +

+ +
<div class="yui3-menubutton-loading">
+    <a id="button-1" href="#menu-1"><span><span>Move To</span></span></a>
+    <div id="menu-1">
+        <ul>
+            <li><input type="button" name="button-1" value="Inbox"></li>
+            <li><input type="button" name="button-2" value="Archive"></li>
+            <li><input type="button" name="button-3" value="Trash"></li>
+        </ul>
+    </div>
+</div>
+ + +

Progressive Enhancement

+ +

+To account for the scenario where the user has CSS enabled in their browser but JavaScript +is disabled, the CSS used to style the menu button will be loaded via JavaScript +using the YUI instance's built-in Loader. +

+ +
YUI({
+    modules: {
+        "menubuttoncss": {
+            type: "css",
+            fullpath: "../assets/node-focusmanager/menubutton.css"
+        },
+        "menubuttonjs": {
+            type: "js",
+            fullpath: "../assets/node-focusmanager/menubutton.js",
+            requires: ["menubuttoncss", "node-focusmanager", "node-event-simulate", "overlay"]
+        }
+    }
+}).use("menubuttonjs");
+ + +

+To prevent the user from seeing a flash unstyled content when JavaScript is enabled, +a style rule is created using YUI's yui3-js-enabled class name that will temporarily +hide the markup while the JavaScript and CSS are in the process of loading. For more on using the +yui3-js-enabled class name, see the +Hiding Progressively Enhanced Markup section of the +YUI Widget landing page. +

+ +
/*  Hide the button and list while it is being transformed into a menu button.  */
+
+.yui3-js-enabled .yui3-menubutton-loading #menu-1,
+.yui3-js-enabled .yui3-menubutton-loading #button-1 {
+    display: none;
+}
+ + +

ARIA Support

+ +

+Through the use of CSS and JavaScript the HTML for the menu button can be +transformed into something that looks and behaves like a desktop menu button, +but users of screen readers won't perceive it as an atomic widget, but rather +simply as a set of HTML elements. However, through the application +of the +WAI-ARIA Roles and States, it is +possible to improve the semantics of the markup such that users of screen +readers perceive it as a menu button control. +

+ +

Keyboard Functionality

+ +

+The keyboard functionality for the button's menu will be provided by the Focus +Manager Node Plugin. The Focus Manager's +descendants +attribute is set to a value of "input", so that only one menuitem in the +button's menu is in the browser's default tab flow. This allows users +navigating via the keyboard to use the tab key to quickly move into and out of +the menu. Once the menu has focus, the user can move focus among each menuitem +using the up and down arrows keys, as defined by the value of the +keys +attribute. The +focusClass +attribute is used to apply a class of yui-menuitem-active to +the parent <li> of each +<input> when it is focused, making it easy to style the +menuitem's focused state in all browsers. + +

YUI().use("*", function (Y) {
+
+    var menuButton = Y.one("#button-1"),
+        menu;
+
+
+    var initMenu = function () {
+
+        menu = new Y.Overlay({
+            contentBox: "#menu-1",
+            visible: false,
+            tabIndex: null
+        });
+
+        menu.render();
+
+
+        Y.one("#menu-1").setStyle("display", "");
+
+        var boundingBox = menu.get("boundingBox"),
+            contentBox = menu.get("contentBox");
+
+        boundingBox.addClass("yui3-buttonmenu");
+        contentBox.addClass("yui3-buttonmenu-content");
+
+
+        // Append a decorator element to the bounding box to render the shadow.
+
+        boundingBox.append('<div class="yui3-menu-shadow"></div>');
+
+
+        //  Apply the ARIA roles, states and properties to the menu.
+
+        boundingBox.setAttrs({
+            role: "menu",
+            "aria-labelledby": menuLabelID
+        });
+
+        boundingBox.all("input").set("role", "menuitem");
+
+
+        //  For NVDA: Add the role of "presentation" to each LI
+        //  element to prevent NVDA from announcing the
+        //  "listitem" role.
+
+        boundingBox.all("div,ul,li").set("role", "presentation");
+
+
+        //  Use the FocusManager Node Plugin to manage the focusability
+        //  of each menuitem in the menu.
+
+        contentBox.plug(Y.Plugin.NodeFocusManager, {
+
+                descendants: "input",
+                keys: { next: "down:40", // Down arrow
+                        previous: "down:38" },  // Up arrow
+                focusClass: {
+                    className: "yui3-menuitem-active",
+                    fn: function (node) {
+                        return node.get("parentNode");
+                    }
+                },
+                circular: true
+
+            });
+
+
+        //  Subscribe to the change event for the "focused" attribute
+        //  to listen for when the menu initially gains focus, and
+        //  when the menu has lost focus completely.
+
+        contentBox.focusManager.after("focusedChange", function (event) {
+
+            if (!event.newVal) {    // The menu has lost focus
+
+                //  Set the "activeDescendant" attribute to 0 when the
+                //  menu is hidden so that the user can tab from the
+                //  button to the first item in the menu the next time
+                //  the menu is made visible.
+
+                this.set("activeDescendant", 0);
+
+            }
+
+        });
+
+
+        //  Hide the button's menu if the user presses the escape key
+        //  while focused either on the button or its menu.
+
+        Y.on("key", function () {
+
+            menu.hide();
+            menuButton.focus();
+
+        }, [menuButton, boundingBox] ,"down:27");
+
+
+        if (Y.UA.ie === 6) {
+
+            //  Set the width and height of the menu's bounding box -
+            //  this is necessary for IE 6 so that the CSS for the
+            //  shadow element can simply set the shadow's width and
+            //  height to 100% to ensure that dimensions of the shadow
+            //  are always sync'd to the that of its parent menu.
+
+            menu.on("visibleChange", function (event) {
+
+                if (event.newVal) {
+
+                    boundingBox.setStyles({ height: "", width: "" });
+
+                    boundingBox.setStyles({
+                        height: (boundingBox.get("offsetHeight") + "px"),
+                        width: (boundingBox.get("offsetWidth") + "px") });
+
+                }
+
+            });
+
+        }
+
+
+        menu.after("visibleChange", function (event) {
+
+            var bVisible = event.newVal;
+
+            //  Focus the first item when the menu is made visible
+            //  to allow users to navigate the menu via the keyboard
+
+            if (bVisible) {
+
+                //  Need to set focus via a timer for Webkit and Opera
+                Y.Lang.later(0, contentBox.focusManager, contentBox.focusManager.focus);
+
+            }
+
+            boundingBox.set("aria-hidden", (!bVisible));
+
+        });
+
+
+        //  Hide the menu when one of menu items is clicked.
+
+        boundingBox.delegate("click", function (event) {
+
+            alert("You clicked " + this.one("input").get("value"));
+
+            contentBox.focusManager.blur();
+            menu.hide();
+
+        }, "li");
+
+
+        //  Focus each menuitem as the user moves the mouse over
+        //  the menu.
+
+        boundingBox.delegate("mouseenter", function (event) {
+
+            var focusManager = contentBox.focusManager;
+
+            if (focusManager.get("focused")) {
+                focusManager.focus(this.one("input"));
+            }
+
+        }, "li");
+
+
+        //  Hide the menu if the user clicks outside of it or if the
+        //  user doesn't click on the button
+
+        boundingBox.get("ownerDocument").on("mousedown", function (event) {
+
+            var oTarget = event.target;
+
+            if (!oTarget.compareTo(menuButton) &&
+                !menuButton.contains(oTarget) &&
+                !oTarget.compareTo(boundingBox) &&
+                !boundingBox.contains(oTarget)) {
+
+                menu.hide();
+
+            }
+
+        });
+
+    };
+
+
+    menuButton.addClass("yui3-menubutton");
+
+
+    //  Hide the list until it is transformed into a menu
+
+    Y.one("#menu-1").setStyle("display", "none");
+
+
+    //  Remove the "yui3-menubutton-loading" class from the parent container
+    //  now that the necessary YUI dependencies are loaded and the
+    //  menu button has been skinned.
+
+    menuButton.ancestor(".yui3-menubutton-loading").removeClass("yui3-menubutton-loading");
+
+
+    //  Apply the ARIA roles, states and properties to the anchor.
+
+    menuButton.setAttrs({
+        role: "button",
+        "aria-haspopup": true
+    });
+
+
+    //  Remove the "href" attribute from the anchor element to
+    //  prevent JAWS and NVDA from reading the value of the "href"
+    //  attribute when the anchor is focused.
+
+    if ((Y.UA.gecko || Y.UA.ie) && navigator.userAgent.indexOf("Windows") > -1) {
+
+        menuButton.removeAttribute("href");
+
+        //  Since the anchor's "href" attribute has been removed, the
+        //  element will not fire the click event in Firefox when the
+        //  user presses the enter key.  To fix this, dispatch the
+        //  "click" event to the anchor when the user presses the
+        //  enter key.
+
+        Y.on("key", function (event) {
+
+            menuButton.simulate("click");
+
+        }, menuButton, "down:13");
+
+    }
+
+
+    //  Set the "tabIndex" attribute of the anchor element to 0 to
+    //  place it in the browser's default tab flow.  This is
+    //  necessary since 1) anchor elements are not in the default
+    //  tab flow in Opera and 2) removing the "href" attribute
+    //  prevents the anchor from firing its "click" event
+    //  in Firefox.
+
+    menuButton.set("tabIndex", 0);
+
+    //  Since there is some intermediary markup (<span>s) between the anchor element with the role
+    //  of "button" applied and the text label for the anchor - we need to use the
+    //  "aria-labelledby" attribute to ensure that screen readers announce the text label for the
+    //  button.
+
+    var menuLabel = menuButton.one("span span"),
+        menuLabelID = Y.stamp(menuLabel);
+
+    menuLabel.set("id", menuLabelID);
+    menuButton.set("aria-labelledby", menuLabelID);
+
+    var showMenu = function (event) {
+
+        //  For performance: Defer the creation of the menu until
+        //  the first time the button is clicked.
+
+        if (!menu) {
+            initMenu();
+        }
+
+
+        if (!menu.get("visible")) {
+
+            menu.set("align", {
+                node: menuButton,
+                points: [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]
+            });
+
+            menu.show();
+
+        }
+
+        //  Prevent the anchor element from being focused
+        //  when the users mouses down on it.
+        event.preventDefault();
+
+    };
+
+
+    //  Bind both a "mousedown" and "click" event listener to
+    //  ensure the button's menu can be invoked using both the
+    //  mouse and the keyboard.
+
+    menuButton.on("mousedown", showMenu);
+    menuButton.on("click", showMenu);
+
+});
+ +
+
+
+ +
+ +
+
+
+ + + + + + + + + + +