_KeyNavContainer.js.uncompressed.js 7.8 KB

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