Skip to content

Commit 68b41d2

Browse files
committed
Since _tabKeyJustPressed is unreliable, track pointer events instead.
Fixes #519.
1 parent f923489 commit 68b41d2

File tree

1 file changed

+22
-28
lines changed

1 file changed

+22
-28
lines changed

KeyNav.js

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -169,15 +169,9 @@ define([
169169
this.keyNavContainerNode = this.containerNode || this;
170170
}
171171

172-
// There are two cases when this.containerNode.focus() is called rather than this.focus():
173-
// - When this is the first element in a Dialog (a11y.getFirstInTabbingOrder()).
174-
// - When tabbing/shift-tabbing from one deliteful/List to another (a11y.getNextInTabbingOrder()).
175-
// In either case, focus my first navigable descendant.
172+
// containerNode.focus() called when this is the first element in a Dialog (a11y.getFirstInTabbingOrder()).
176173
if (this.containerNode && this.containerNode !== this) {
177174
this.containerNode.focus = function () {
178-
// Avoid focusinHandler() making recursive call to focus().
179-
this._tabKeyJustPressed = false;
180-
181175
// Focus first navigable descendant.
182176
this.focus();
183177
}.bind(this);
@@ -187,6 +181,7 @@ define([
187181
this.on("keydown", this._keynavKeyDownHandler.bind(this), this.keyNavContainerNode);
188182

189183
this.on("pointerdown", this.pointerdownHandler.bind(this), this.keyNavContainerNode);
184+
this.on("pointerup", this.pointerupHandler.bind(this), this.keyNavContainerNode);
190185
this.on("focusin", this.focusinHandler.bind(this), this.keyNavContainerNode);
191186
this.on("focusout", this.focusoutHandler.bind(this), this.keyNavContainerNode);
192187
},
@@ -212,45 +207,42 @@ define([
212207
if (this.focusDescendants && !container.hasAttribute("tabindex")) {
213208
container.tabIndex = "0";
214209
}
215-
216-
// Keep track of if user just pressed tab, so we can have different focus behavior for mouse vs. keyboard.
217-
this._keynavTabKeyListener = on(this.ownerDocument.body, "keydown", function (evt) {
218-
if (evt.key === "Tab") {
219-
this._tabKeyJustPressed = true;
220-
221-
// Clear _tabKeyJustPressed after focusinHandler() has been called. Note that 0ms is not long
222-
// enough in some cases on Firefox, although that problem doesn't reproduce in the delite tests.
223-
this.defer(function () {
224-
this._tabKeyJustPressed = false;
225-
}, 10);
226-
}
227-
}.bind(this), true);
228210
}),
229211

230-
disconnectedCallback: function () {
231-
// Remove listener here rather than using this.own() so that there's no need to call destroy().
232-
this._keynavTabKeyListener.remove();
233-
},
234-
235212
/**
236213
* Called on pointerdown event (on container or child of container).
237-
* Navigation occurs on pointerdown, to match behavior of native elements.
238-
* Normally this handler isn't needed as it's redundant w/the focusin event.
239214
*/
240215
pointerdownHandler: function (evt) {
216+
// Focusin handler needs to differentiate between focusin from pointer or tab/shift-tab.
217+
this._pointerOperation = true;
218+
219+
// Navigation occurs on pointerdown, to match behavior of native elements.
220+
// Normally this handler isn't needed as it's redundant w/the focusin event.
241221
var target = this._getTargetElement(evt);
242222
if (target) {
243223
this._descendantNavigateHandler(target, evt);
244224
}
245225
},
246226

227+
/**
228+
* Called on pointerup event (on container or child of container).
229+
*/
230+
pointerupHandler: function () {
231+
// Clear _pointerOperation after focusinHandler() has been called. Note that 0ms is not long
232+
// enough in some cases on Firefox, although that problem doesn't reproduce in the delite tests.
233+
this.defer(function () {
234+
delete this._pointerOperation;
235+
}, 10);
236+
},
237+
247238
/**
248239
* Called on focus of container or child of container.
249240
*/
250241
focusinHandler: function (evt) {
251242
var container = this.keyNavContainerNode;
252243
if (this.focusDescendants) {
253-
if (this._tabKeyJustPressed && !container.contains(evt.relatedTarget)) {
244+
var focusByTab = !this._pointerOperation && !this._programmaticallyFocusing;
245+
if (focusByTab && !container.contains(evt.relatedTarget)) {
254246
// When tabbing/shift-tabbing into this widget, focus the first child but do it on a delay so that
255247
// activationTracker sees my "focus" event before seeing the "focus" event on the child widget.
256248
// Note that shift-tab (from outside this widget) might go to an embedded widget rather than
@@ -384,7 +376,9 @@ define([
384376
// If this._savedTabIndex is set, use it instead of this.tabIndex, because it means
385377
// the container's tabIndex has already been changed to -1.
386378
child.tabIndex = "_savedTabIndex" in this ? this._savedTabIndex : this.keyNavContainerNode.tabIndex;
379+
this._programmaticallyFocusing = true;
387380
child.focus();
381+
delete this._programmaticallyFocusing;
388382

389383
// _descendantNavigateHandler() will be called automatically from child's focus event.
390384
} else {

0 commit comments

Comments
 (0)