From d0f659bc28166737cf073ac08040219d3ce36c37 Mon Sep 17 00:00:00 2001 From: Eugene Kashida Date: Sun, 17 Mar 2019 23:51:25 -0700 Subject: [PATCH] refactor(engine): prevent focus events when engine manages focus --- packages/@lwc/engine/src/faux-shadow/focus.ts | 59 +++++++++++-------- .../integration/issue-1031/issue-1031.html | 3 + .../integration/issue-1031/issue-1031.js | 4 ++ .../test-issue-1031/issue-1031.spec.js | 5 +- 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/packages/@lwc/engine/src/faux-shadow/focus.ts b/packages/@lwc/engine/src/faux-shadow/focus.ts index 81b3ab7735..67734f8ead 100644 --- a/packages/@lwc/engine/src/faux-shadow/focus.ts +++ b/packages/@lwc/engine/src/faux-shadow/focus.ts @@ -5,6 +5,7 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ import assert from '../shared/assert'; +import { windowAddEventListener, windowRemoveEventListener } from '../env/window'; import { matches, querySelectorAll, @@ -206,24 +207,40 @@ function getNextTabbableElement(segments: QuerySegments): HTMLElement | null { return getFirstTabbableMatch(next); } +function muteEvent(event) { + event.preventDefault(); + event.stopPropagation(); +} +function muteFocusEventsDuringExecution(func: Function) { + windowAddEventListener.call(window, 'focusin', muteEvent, true); + windowAddEventListener.call(window, 'focusout', muteEvent, true); + func(); + windowRemoveEventListener.call(window, 'focusin', muteEvent, true); + windowRemoveEventListener.call(window, 'focusout', muteEvent, true); +} + function focusOnNextOrBlur(focusEventTarget: EventTarget, segments: QuerySegments) { - const nextNode = getNextTabbableElement(segments); - if (isNull(nextNode)) { - // nothing to focus on, blur to invalidate the operation - (focusEventTarget as HTMLElement).blur(); - return; - } - nextNode.focus(); + muteFocusEventsDuringExecution(() => { + const nextNode = getNextTabbableElement(segments); + if (isNull(nextNode)) { + // nothing to focus on, blur to invalidate the operation + (focusEventTarget as HTMLElement).blur(); + } else { + nextNode.focus(); + } + }); } function focusOnPrevOrBlur(focusEventTarget: EventTarget, segments: QuerySegments) { - const prevNode = getPreviousTabbableElement(segments); - if (isNull(prevNode)) { - // nothing to focus on, blur to invalidate the operation - (focusEventTarget as HTMLElement).blur(); - return; - } - prevNode.focus(); + muteFocusEventsDuringExecution(() => { + const prevNode = getPreviousTabbableElement(segments); + if (isNull(prevNode)) { + // nothing to focus on, blur to invalidate the operation + (focusEventTarget as HTMLElement).blur(); + } else { + prevNode.focus(); + } + }); } function isFirstTabbableChild(target: EventTarget, segments: QuerySegments): boolean { @@ -260,7 +277,9 @@ function keyboardFocusHandler(event: FocusEvent) { // probably tabbing into element const first = getFirstTabbableMatch(segments.inner); if (!isNull(first)) { - first.focus(); + muteFocusEventsDuringExecution(() => { + first.focus(); + }); } else { focusOnNextOrBlur(target, segments); } @@ -302,20 +321,14 @@ function keyboardFocusInHandler(event: FocusEvent) { const post = relatedTargetPosition(host as HTMLElement, relatedTarget); switch (post) { case 1: // focus is probably coming from above - if ( - isFirstFocusableChildReceivingFocus && - relatedTarget === getPreviousTabbableElement(segments) - ) { + if (isFirstFocusableChildReceivingFocus) { // the focus was on the immediate focusable elements from above, // it is almost certain that the focus is due to tab keypress focusOnNextOrBlur(target, segments); } break; case 2: // focus is probably coming from below - if ( - isLastFocusableChildReceivingFocus && - relatedTarget === getNextTabbableElement(segments) - ) { + if (isLastFocusableChildReceivingFocus) { // the focus was on the immediate focusable elements from above, // it is almost certain that the focus is due to tab keypress focusOnPrevOrBlur(target, segments); diff --git a/packages/integration-tests/src/components/delegates-focus-tab-navigation/test-issue-1031/integration/issue-1031/issue-1031.html b/packages/integration-tests/src/components/delegates-focus-tab-navigation/test-issue-1031/integration/issue-1031/issue-1031.html index cb7fd0525a..8a53b6ddf6 100644 --- a/packages/integration-tests/src/components/delegates-focus-tab-navigation/test-issue-1031/integration/issue-1031/issue-1031.html +++ b/packages/integration-tests/src/components/delegates-focus-tab-navigation/test-issue-1031/integration/issue-1031/issue-1031.html @@ -1,4 +1,7 @@