Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 cr.define('cr.ui', function() { | 5 cr.define('cr.ui', function() { |
| 6 /** | 6 /** |
| 7 * Constructor for FocusManager singleton. Checks focus of elements to ensure | 7 * Constructor for FocusManager singleton. Checks focus of elements to ensure |
| 8 * that elements in "background" pages (i.e., those in a dialog that is not | 8 * that elements in "background" pages (i.e., those in a dialog that is not |
| 9 * the topmost overlay) do not receive focus. | 9 * the topmost overlay) do not receive focus. |
| 10 * @constructor | 10 * @constructor |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 59 * @return {Array.Element} The focusable elements. | 59 * @return {Array.Element} The focusable elements. |
| 60 */ | 60 */ |
| 61 getFocusableElements_: function() { | 61 getFocusableElements_: function() { |
| 62 var focusableDiv = this.getFocusParent(); | 62 var focusableDiv = this.getFocusParent(); |
| 63 | 63 |
| 64 // Create a TreeWalker object to traverse the DOM from |focusableDiv|. | 64 // Create a TreeWalker object to traverse the DOM from |focusableDiv|. |
| 65 var treeWalker = document.createTreeWalker( | 65 var treeWalker = document.createTreeWalker( |
| 66 focusableDiv, | 66 focusableDiv, |
| 67 NodeFilter.SHOW_ELEMENT, | 67 NodeFilter.SHOW_ELEMENT, |
| 68 { acceptNode: function(node) { | 68 { acceptNode: function(node) { |
| 69 var style = window.getComputedStyle(node); | |
| 69 // Reject all hidden nodes. FILTER_REJECT also rejects these | 70 // Reject all hidden nodes. FILTER_REJECT also rejects these |
| 70 // nodes' children, so non-hidden elements that are descendants of | 71 // nodes' children, so non-hidden elements that are descendants of |
| 71 // hidden <div>s will correctly be rejected. | 72 // hidden <div>s will correctly be rejected. |
| 72 if (node.hidden) | 73 if (node.hidden || style.display == 'none' || |
| 74 style.visibility == 'hidden') | |
|
Evan Stade
2012/09/21 12:12:11
curlies
bartfab (slow)
2012/09/24 17:15:55
Done.
| |
| 73 return NodeFilter.FILTER_REJECT; | 75 return NodeFilter.FILTER_REJECT; |
| 74 | 76 |
| 75 // Skip nodes that cannot receive focus. FILTER_SKIP does not | 77 // Skip nodes that cannot receive focus. FILTER_SKIP does not |
| 76 // cause this node's children also to be skipped. | 78 // cause this node's children also to be skipped. |
| 77 if (node.disabled || node.tabIndex < 0) | 79 if (node.disabled || node.tabIndex < 0) |
| 78 return NodeFilter.FILTER_SKIP; | 80 return NodeFilter.FILTER_SKIP; |
| 79 | 81 |
| 80 // Accept nodes that are non-hidden and focusable. | 82 // Accept nodes that are non-hidden and focusable. |
| 81 return NodeFilter.FILTER_ACCEPT; | 83 return NodeFilter.FILTER_ACCEPT; |
| 82 } | 84 } |
| 83 }, | 85 }, |
| 84 false); | 86 false); |
| 85 | 87 |
| 86 var focusable = []; | 88 var focusable = []; |
| 87 while (treeWalker.nextNode()) | 89 while (treeWalker.nextNode()) |
| 88 focusable.push(treeWalker.currentNode); | 90 focusable.push(treeWalker.currentNode); |
| 89 | 91 |
| 90 return focusable; | 92 return focusable; |
| 91 }, | 93 }, |
| 92 | 94 |
| 93 /** | 95 /** |
| 94 * Retrieves the page's focusable elements and, if there are any, focuses | 96 * Dispatches an 'elementFocused' event to notify an element that it has |
| 95 * the first one. | 97 * received focus. When focus wraps around within the a page, only the |
| 98 * element that has focus after the wrapping receives an 'elementFocused' | |
| 99 * event. This differs from the native 'focus' event which is received by | |
| 100 * an element outside the page first, followed by a 'focus' on an element | |
| 101 * within the page after the FocusManager has intervened. | |
| 102 * @param {Element} element The element that has received focus. | |
| 96 * @private | 103 * @private |
| 97 */ | 104 */ |
| 98 focusFirstElement_: function() { | 105 dispatchFocusEvent_: function(element) { |
| 99 var focusableElements = this.getFocusableElements_(); | 106 cr.dispatchSimpleEvent(element, 'elementFocused', true, false); |
| 100 if (focusableElements.length != 0) | |
| 101 focusableElements[0].focus(); | |
| 102 }, | 107 }, |
| 103 | 108 |
| 104 /** | 109 /** |
| 105 * Retrieves the page's focusable elements and, if there are any, focuses | |
| 106 * the last one. | |
| 107 * @private | |
| 108 */ | |
| 109 focusLastElement_: function() { | |
| 110 var focusableElements = this.getFocusableElements_(); | |
| 111 if (focusableElements.length != 0) | |
| 112 focusableElements[focusableElements.length - 1].focus(); | |
| 113 }, | |
| 114 | |
| 115 /** | |
| 116 * Attempts to focus the appropriate element in the current dialog. | 110 * Attempts to focus the appropriate element in the current dialog. |
| 117 * @private | 111 * @private |
| 118 */ | 112 */ |
| 119 setFocus_: function() { | 113 setFocus_: function() { |
| 120 // If |this.focusDirBackwards_| is true, the user has pressed "Shift+Tab" | 114 // If |this.focusDirBackwards_| is true, the user has pressed "Shift+Tab" |
| 121 // and has caused the focus to be transferred backward, outside of the | 115 // and has caused the focus to be transferred backward, outside of the |
| 122 // current dialog. In this case, loop around and try to focus the last | 116 // current dialog. In this case, loop around and try to focus the last |
| 123 // element of the dialog; otherwise, try to focus the first element of the | 117 // element of the dialog; otherwise, try to focus the first element of the |
| 124 // dialog. | 118 // dialog. |
| 125 if (this.focusDirBackwards_) | 119 var focusableElements = this.getFocusableElements_(); |
| 126 this.focusLastElement_(); | 120 var element = this.focusDirBackwards_ ? focusableElements.pop() : |
| 127 else | 121 focusableElements.shift(); |
| 128 this.focusFirstElement_(); | 122 if (element) { |
| 123 element.focus(); | |
| 124 this.dispatchFocusEvent_(element); | |
| 125 } | |
| 129 }, | 126 }, |
| 130 | 127 |
| 131 /** | 128 /** |
| 132 * Attempts to focus the first element in the current dialog. | 129 * Attempts to focus the first element in the current dialog. |
| 133 */ | 130 */ |
| 134 focusFirstElement: function() { | 131 focusFirstElement: function() { |
| 135 this.focusFirstElement_(); | 132 this.focusFirstElement_(); |
| 136 }, | 133 }, |
| 137 | 134 |
| 138 /** | 135 /** |
| 139 * Handler for focus events on the page. | 136 * Handler for focus events on the page. |
| 140 * @param {Event} event The focus event. | 137 * @param {Event} event The focus event. |
| 141 * @private | 138 * @private |
| 142 */ | 139 */ |
| 143 onDocumentFocus_: function(event) { | 140 onDocumentFocus_: function(event) { |
| 144 // If the element being focused is a descendant of the currently visible | 141 // If the element being focused is a descendant of the currently visible |
| 145 // page, focus is valid. | 142 // page, focus is valid. |
| 146 if (this.isDescendantOf_(this.getFocusParent(), event.target)) | 143 if (this.isDescendantOf_(this.getFocusParent(), event.target)) { |
| 144 this.dispatchFocusEvent_(event.target); | |
| 147 return; | 145 return; |
| 146 } | |
| 148 | 147 |
| 149 // The target of the focus event is not in the topmost visible page and | 148 // The target of the focus event is not in the topmost visible page and |
| 150 // should not be focused. | 149 // should not be focused. |
| 151 event.target.blur(); | 150 event.target.blur(); |
| 152 | 151 |
| 152 // Attempt to wrap around focus within the current page. | |
| 153 this.setFocus_(); | 153 this.setFocus_(); |
| 154 }, | 154 }, |
| 155 | 155 |
| 156 /** | 156 /** |
| 157 * Handler for keydown events on the page. | 157 * Handler for keydown events on the page. |
| 158 * @param {Event} event The keydown event. | 158 * @param {Event} event The keydown event. |
| 159 * @private | 159 * @private |
| 160 */ | 160 */ |
| 161 onDocumentKeyDown_: function(event) { | 161 onDocumentKeyDown_: function(event) { |
| 162 /** @const */ var tabKeyCode = 9; | 162 /** @const */ var tabKeyCode = 9; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 176 true); | 176 true); |
| 177 document.addEventListener('keydown', this.onDocumentKeyDown_.bind(this), | 177 document.addEventListener('keydown', this.onDocumentKeyDown_.bind(this), |
| 178 true); | 178 true); |
| 179 }, | 179 }, |
| 180 }; | 180 }; |
| 181 | 181 |
| 182 return { | 182 return { |
| 183 FocusManager: FocusManager, | 183 FocusManager: FocusManager, |
| 184 }; | 184 }; |
| 185 }); | 185 }); |
| OLD | NEW |