_KeyNavContainer.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. define("dijit/_KeyNavContainer", [
  2. "dojo/_base/kernel", // kernel.deprecated
  3. "./_Container",
  4. "./_FocusMixin",
  5. "dojo/_base/array", // array.forEach
  6. "dojo/keys", // keys.END keys.HOME
  7. "dojo/_base/declare", // declare
  8. "dojo/_base/event", // event.stop
  9. "dojo/dom-attr", // domAttr.set
  10. "dojo/_base/lang" // lang.hitch
  11. ], function(kernel, _Container, _FocusMixin, array, keys, declare, event, domAttr, lang){
  12. /*=====
  13. var _FocusMixin = dijit._FocusMixin;
  14. var _Container = dijit._Container;
  15. =====*/
  16. // module:
  17. // dijit/_KeyNavContainer
  18. // summary:
  19. // A _Container with keyboard navigation of its children.
  20. return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
  21. // summary:
  22. // A _Container with keyboard navigation of its children.
  23. // description:
  24. // To use this mixin, call connectKeyNavHandlers() in
  25. // postCreate().
  26. // It provides normalized keyboard and focusing code for Container
  27. // widgets.
  28. /*=====
  29. // focusedChild: [protected] Widget
  30. // The currently focused child widget, or null if there isn't one
  31. focusedChild: null,
  32. =====*/
  33. // tabIndex: Integer
  34. // Tab index of the container; same as HTML tabIndex attribute.
  35. // Note then when user tabs into the container, focus is immediately
  36. // moved to the first item in the container.
  37. tabIndex: "0",
  38. connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
  39. // summary:
  40. // Call in postCreate() to attach the keyboard handlers
  41. // to the container.
  42. // preKeyCodes: keys[]
  43. // Key codes for navigating to the previous child.
  44. // nextKeyCodes: keys[]
  45. // Key codes for navigating to the next child.
  46. // tags:
  47. // protected
  48. // TODO: call this automatically from my own postCreate()
  49. var keyCodes = (this._keyNavCodes = {});
  50. var prev = lang.hitch(this, "focusPrev");
  51. var next = lang.hitch(this, "focusNext");
  52. array.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
  53. array.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
  54. keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
  55. keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
  56. this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
  57. this.connect(this.domNode, "onfocus", "_onContainerFocus");
  58. },
  59. startupKeyNavChildren: function(){
  60. kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
  61. },
  62. startup: function(){
  63. this.inherited(arguments);
  64. array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
  65. },
  66. addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
  67. this.inherited(arguments);
  68. this._startupChild(widget);
  69. },
  70. focus: function(){
  71. // summary:
  72. // Default focus() implementation: focus the first child.
  73. this.focusFirstChild();
  74. },
  75. focusFirstChild: function(){
  76. // summary:
  77. // Focus the first focusable child in the container.
  78. // tags:
  79. // protected
  80. this.focusChild(this._getFirstFocusableChild());
  81. },
  82. focusLastChild: function(){
  83. // summary:
  84. // Focus the last focusable child in the container.
  85. // tags:
  86. // protected
  87. this.focusChild(this._getLastFocusableChild());
  88. },
  89. focusNext: function(){
  90. // summary:
  91. // Focus the next widget
  92. // tags:
  93. // protected
  94. this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
  95. },
  96. focusPrev: function(){
  97. // summary:
  98. // Focus the last focusable node in the previous widget
  99. // (ex: go to the ComboButton icon section rather than button section)
  100. // tags:
  101. // protected
  102. this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
  103. },
  104. focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
  105. // summary:
  106. // Focus specified child widget.
  107. // widget:
  108. // Reference to container's child widget
  109. // last:
  110. // If true and if widget has multiple focusable nodes, focus the
  111. // last one instead of the first one
  112. // tags:
  113. // protected
  114. if(!widget){ return; }
  115. if(this.focusedChild && widget !== this.focusedChild){
  116. this._onChildBlur(this.focusedChild); // used by _MenuBase
  117. }
  118. widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
  119. widget.focus(last ? "end" : "start");
  120. this._set("focusedChild", widget);
  121. },
  122. _startupChild: function(/*dijit._Widget*/ widget){
  123. // summary:
  124. // Setup for each child widget
  125. // description:
  126. // Sets tabIndex=-1 on each child, so that the tab key will
  127. // leave the container rather than visiting each child.
  128. // tags:
  129. // private
  130. widget.set("tabIndex", "-1");
  131. this.connect(widget, "_onFocus", function(){
  132. // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
  133. widget.set("tabIndex", this.tabIndex);
  134. });
  135. this.connect(widget, "_onBlur", function(){
  136. widget.set("tabIndex", "-1");
  137. });
  138. },
  139. _onContainerFocus: function(evt){
  140. // summary:
  141. // Handler for when the container gets focus
  142. // description:
  143. // Initially the container itself has a tabIndex, but when it gets
  144. // focus, switch focus to first child...
  145. // tags:
  146. // private
  147. // Note that we can't use _onFocus() because switching focus from the
  148. // _onFocus() handler confuses the focus.js code
  149. // (because it causes _onFocusNode() to be called recursively)
  150. // Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
  151. // Ignore spurious focus events:
  152. // 1. focus on a child widget bubbles on FF
  153. // 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
  154. if(evt.target !== this.domNode || this.focusedChild){ return; }
  155. this.focusFirstChild();
  156. // and then set the container's tabIndex to -1,
  157. // (don't remove as that breaks Safari 4)
  158. // so that tab or shift-tab will go to the fields after/before
  159. // the container, rather than the container itself
  160. domAttr.set(this.domNode, "tabIndex", "-1");
  161. },
  162. _onBlur: function(evt){
  163. // When focus is moved away the container, and its descendant (popup) widgets,
  164. // then restore the container's tabIndex so that user can tab to it again.
  165. // Note that using _onBlur() so that this doesn't happen when focus is shifted
  166. // to one of my child widgets (typically a popup)
  167. if(this.tabIndex){
  168. domAttr.set(this.domNode, "tabIndex", this.tabIndex);
  169. }
  170. this.focusedChild = null;
  171. this.inherited(arguments);
  172. },
  173. _onContainerKeypress: function(evt){
  174. // summary:
  175. // When a key is pressed, if it's an arrow key etc. then
  176. // it's handled here.
  177. // tags:
  178. // private
  179. if(evt.ctrlKey || evt.altKey){ return; }
  180. var func = this._keyNavCodes[evt.charOrCode];
  181. if(func){
  182. func();
  183. event.stop(evt);
  184. }
  185. },
  186. _onChildBlur: function(/*dijit._Widget*/ /*===== widget =====*/){
  187. // summary:
  188. // Called when focus leaves a child widget to go
  189. // to a sibling widget.
  190. // Used by MenuBase.js (TODO: move code there)
  191. // tags:
  192. // protected
  193. },
  194. _getFirstFocusableChild: function(){
  195. // summary:
  196. // Returns first child that can be focused
  197. return this._getNextFocusableChild(null, 1); // dijit._Widget
  198. },
  199. _getLastFocusableChild: function(){
  200. // summary:
  201. // Returns last child that can be focused
  202. return this._getNextFocusableChild(null, -1); // dijit._Widget
  203. },
  204. _getNextFocusableChild: function(child, dir){
  205. // summary:
  206. // Returns the next or previous focusable child, compared
  207. // to "child"
  208. // child: Widget
  209. // The current widget
  210. // dir: Integer
  211. // * 1 = after
  212. // * -1 = before
  213. if(child){
  214. child = this._getSiblingOfChild(child, dir);
  215. }
  216. var children = this.getChildren();
  217. for(var i=0; i < children.length; i++){
  218. if(!child){
  219. child = children[(dir>0) ? 0 : (children.length-1)];
  220. }
  221. if(child.isFocusable()){
  222. return child; // dijit._Widget
  223. }
  224. child = this._getSiblingOfChild(child, dir);
  225. }
  226. // no focusable child found
  227. return null; // dijit._Widget
  228. }
  229. });
  230. });