1 /** 2 * @fileOverview http://www.w3.org/TR/wai-aria-practices/#trap_focus 3 * @author yiminghe@gmail.com 4 */ 5 KISSY.add("overlay/ariaRender", function (S, Node) { 6 7 var $ = Node.all; 8 9 function Aria() { 10 } 11 12 13 var KEY_TAB = Node.KeyCodes.TAB; 14 15 function _onKey(/*Normalized Event*/ evt) { 16 17 var self = this, 18 keyCode = evt.keyCode, 19 firstFocusItem = self.get("el"); 20 if (keyCode != KEY_TAB) { 21 return; 22 } 23 // summary: 24 // Handles the keyboard events for accessibility reasons 25 26 var node = $(evt.target); // get the target node of the keypress event 27 28 // find the first and last tab focusable items in the hierarchy of the dialog container node 29 // do this every time if the items may be added / removed from the the dialog may change visibility or state 30 31 var lastFocusItem = self.__ariaArchor; 32 33 // assumes firstFocusItem and lastFocusItem maintained by dialog object 34 35 // see if we are shift-tabbing from first focusable item on dialog 36 if (node.equals(firstFocusItem) && evt.shiftKey) { 37 lastFocusItem[0].focus(); // send focus to last item in dialog 38 evt.halt(); //stop the tab keypress event 39 } 40 // see if we are tabbing from the last focusable item 41 else if (node.equals(lastFocusItem) && !evt.shiftKey) { 42 firstFocusItem[0].focus(); // send focus to first item in dialog 43 evt.halt(); //stop the tab keypress event 44 } 45 else { 46 // see if the key is for the dialog 47 if (node.equals(firstFocusItem) || 48 firstFocusItem.contains(node)) { 49 return; 50 } 51 } 52 // this key is for the document window 53 // allow tabbing into the dialog 54 evt.halt();//stop the event if not a tab keypress 55 } // end of function 56 57 58 Aria.prototype = { 59 60 __renderUI:function () { 61 var self = this, 62 el = self.get("el"), 63 header = self.get("header"); 64 if (self.get("aria")) { 65 el.attr("role", "dialog"); 66 el.attr("tabindex", 0); 67 if (!header.attr("id")) { 68 header.attr("id", S.guid("ks-dialog-header")); 69 } 70 el.attr("aria-labelledby", header.attr("id")); 71 // 哨兵元素,从这里 tab 出去到弹窗根节点 72 // 从根节点 shift tab 出去到这里 73 self.__ariaArchor = $("<div tabindex='0'></div>").appendTo(el); 74 } 75 }, 76 77 __bindUI:function () { 78 79 var self = this; 80 if (self.get("aria")) { 81 var el = self.get("el"), 82 lastActive; 83 self.on("afterVisibleChange", function (ev) { 84 if (ev.newVal) { 85 lastActive = el[0].ownerDocument.activeElement; 86 el[0].focus(); 87 el.attr("aria-hidden", "false"); 88 el.on("keydown", _onKey, self); 89 } else { 90 el.attr("aria-hidden", "true"); 91 el.detach("keydown", _onKey, self); 92 lastActive && lastActive.focus(); 93 } 94 }); 95 } 96 } 97 }; 98 99 return Aria; 100 }, { 101 requires:["node"] 102 });