1 define("dijit/a11yclick", [
3 "dojo/_base/array", // array.forEach
4 "dojo/keys", // keys.ENTER keys.SPACE
5 "dojo/_base/declare", // declare
6 "dojo/has", // has("dom-addeventlistener")
7 "dojo/_base/unload", // unload.addOnWindowUnload
8 "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
9 ], function(on, array, keys, declare, has, unload, win){
14 // Keep track of where the last keydown event was, to help avoid generating
15 // spurious ondijitclick events when:
16 // 1. focus is on a <button> or <a>
17 // 2. user presses then releases the ENTER key
18 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
19 // 4. onkeyup event fires, causing the ondijitclick handler to fire
20 var lastKeyDownNode = null;
21 if(has("dom-addeventlistener")){
22 win.doc.addEventListener('keydown', function(evt){
23 lastKeyDownNode = evt.target;
26 // Fallback path for IE6-8
28 var keydownCallback = function(evt){
29 lastKeyDownNode = evt.srcElement;
31 win.doc.attachEvent('onkeydown', keydownCallback);
32 unload.addOnWindowUnload(function(){
33 win.doc.detachEvent('onkeydown', keydownCallback);
38 function clickKey(/*Event*/ e){
39 return (e.keyCode === keys.ENTER || e.keyCode === keys.SPACE) &&
40 !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
43 return function(node, listener){
45 // Custom a11yclick (a.k.a. ondijitclick) event
46 // which triggers on a mouse click, touch, or space/enter keyup.
48 if(/input|button/i.test(node.nodeName)){
49 // pass through, the browser already generates click event on SPACE/ENTER key
50 return on(node, "click", listener);
52 // Don't fire the click event unless both the keydown and keyup occur on this node.
53 // Avoids problems where focus shifted to this node or away from the node on keydown,
54 // either causing this node to process a stray keyup event, or causing another node
55 // to get a stray keyup event.
58 on(node, "keydown", function(e){
59 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
61 // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
62 lastKeyDownNode = e.target;
64 // Prevent viewport scrolling on space key in IE<9.
65 // (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
70 on(node, "keyup", function(e){
71 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
72 if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
73 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
74 lastKeyDownNode = null;
75 on.emit(e.target, "click", {
82 on(node, "click", function(e){
83 // catch mouse clicks, plus the on.emit() calls from above and below
84 listener.call(this, e);
89 // touchstart-->touchend will automatically generate a click event, but there are problems
90 // on iOS after focus has been programatically shifted (#14604, #14918), so setup a failsafe
91 // if click doesn't fire naturally.
95 on(node, "touchend", function(e){
96 var target = e.target;
97 clickTimer = setTimeout(function(){
99 on.emit(target, "click", {
105 on(node, "click", function(e){
106 // If browser generates a click naturally, clear the timer to fire a synthetic click event
108 clearTimeout(clickTimer);
111 // TODO: if the touchstart and touchend were <100ms apart, and then there's another touchstart
112 // event <300ms after the touchend event, then clear the synthetic click timer, because user
113 // is doing a zoom. Alternately monitor screen.deviceXDPI (or something similar) to see if
114 // zoom level has changed.
120 array.forEach(handles, function(h){ h.remove(); });
122 clearTimeout(clickTimer);