a11yclick.js.uncompressed.js 4.5 KB

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