_OnDijitClickMixin.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. define("dijit/_OnDijitClickMixin", [
  2. "dojo/on",
  3. "dojo/_base/array", // array.forEach
  4. "dojo/keys", // keys.ENTER keys.SPACE
  5. "dojo/_base/declare", // declare
  6. "dojo/_base/sniff", // has("ie")
  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/_OnDijitClickMixin
  12. // summary:
  13. // Mixin so you can pass "ondijitclick" to this.connect() method,
  14. // as a way to handle clicks by mouse, or by keyboard (SPACE/ENTER key)
  15. // Keep track of where the last keydown event was, to help avoid generating
  16. // spurious ondijitclick events when:
  17. // 1. focus is on a <button> or <a>
  18. // 2. user presses then releases the ENTER key
  19. // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
  20. // 4. onkeyup event fires, causing the ondijitclick handler to fire
  21. var lastKeyDownNode = null;
  22. if(has("ie") < 9){
  23. (function(){
  24. var keydownCallback = function(evt){
  25. lastKeyDownNode = evt.srcElement;
  26. };
  27. win.doc.attachEvent('onkeydown', keydownCallback);
  28. unload.addOnWindowUnload(function(){
  29. win.doc.detachEvent('onkeydown', keydownCallback);
  30. });
  31. })();
  32. }else{
  33. win.doc.addEventListener('keydown', function(evt){
  34. lastKeyDownNode = evt.target;
  35. }, true);
  36. }
  37. // Custom a11yclick (a.k.a. ondijitclick) event
  38. var a11yclick = function(node, listener){
  39. if(/input|button/i.test(node.nodeName)){
  40. // pass through, the browser already generates click event on SPACE/ENTER key
  41. return on(node, "click", listener);
  42. }else{
  43. // Don't fire the click event unless both the keydown and keyup occur on this node.
  44. // Avoids problems where focus shifted to this node or away from the node on keydown,
  45. // either causing this node to process a stray keyup event, or causing another node
  46. // to get a stray keyup event.
  47. function clickKey(/*Event*/ e){
  48. return (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) &&
  49. !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
  50. }
  51. var handles = [
  52. on(node, "keypress", 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. // Do this onkeypress rather than onkeydown because onkeydown.preventDefault() will
  60. // suppress the onkeypress event, breaking _HasDropDown
  61. e.preventDefault();
  62. }
  63. }),
  64. on(node, "keyup", function(e){
  65. //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
  66. if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
  67. //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
  68. lastKeyDownNode = null;
  69. listener.call(this, e);
  70. }
  71. }),
  72. on(node, "click", function(e){
  73. // and connect for mouse clicks too (or touch-clicks on mobile)
  74. listener.call(this, e);
  75. })
  76. ];
  77. return {
  78. remove: function(){
  79. array.forEach(handles, function(h){ h.remove(); });
  80. }
  81. };
  82. }
  83. };
  84. return declare("dijit._OnDijitClickMixin", null, {
  85. connect: function(
  86. /*Object|null*/ obj,
  87. /*String|Function*/ event,
  88. /*String|Function*/ method){
  89. // summary:
  90. // Connects specified obj/event to specified method of this object
  91. // and registers for disconnect() on widget destroy.
  92. // description:
  93. // Provide widget-specific analog to connect.connect, except with the
  94. // implicit use of this widget as the target object.
  95. // This version of connect also provides a special "ondijitclick"
  96. // event which triggers on a click or space or enter keyup.
  97. // Events connected with `this.connect` are disconnected upon
  98. // destruction.
  99. // returns:
  100. // A handle that can be passed to `disconnect` in order to disconnect before
  101. // the widget is destroyed.
  102. // example:
  103. // | var btn = new dijit.form.Button();
  104. // | // when foo.bar() is called, call the listener we're going to
  105. // | // provide in the scope of btn
  106. // | btn.connect(foo, "bar", function(){
  107. // | console.debug(this.toString());
  108. // | });
  109. // tags:
  110. // protected
  111. return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
  112. }
  113. });
  114. });