From 0552d190b0ff635200fc4df0020f02aebee83c6d Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Mon, 27 Nov 2023 18:16:10 +0800 Subject: [PATCH] fix: properly unregister keydown event on top-level document on stop --- src/navigator/IFrameNavigator.ts | 3 + src/utils/KeyboardEventHandler.ts | 108 +++++++++++++++++------------- 2 files changed, 66 insertions(+), 45 deletions(-) diff --git a/src/navigator/IFrameNavigator.ts b/src/navigator/IFrameNavigator.ts index fc5baedc..9c3a519f 100644 --- a/src/navigator/IFrameNavigator.ts +++ b/src/navigator/IFrameNavigator.ts @@ -523,6 +523,9 @@ export class IFrameNavigator extends EventEmitter implements Navigator { this.iframes.forEach((iframe) => { removeEventListenerOptional(iframe, "resize", this.onResize); }); + + if (this.didInitKeyboardEventHandler) + this.keyboardEventHandler.removeEvents(document); } spreads: HTMLDivElement; firstSpread: HTMLDivElement; diff --git a/src/utils/KeyboardEventHandler.ts b/src/utils/KeyboardEventHandler.ts index c28a2c93..76dcaab8 100644 --- a/src/utils/KeyboardEventHandler.ts +++ b/src/utils/KeyboardEventHandler.ts @@ -35,59 +35,77 @@ export default class KeyboardEventHandler { } }; + public removeEvents = (element: HTMLElement | Document | null): void => { + if (element) { + const self = this; + element.removeEventListener("focusin", this.onFocusIn(self), true); + element.removeEventListener("keydown", this.onKeyDown(self), false); + } + }; + public focusin = (element: HTMLElement | Document): void => { const self = this; - element.addEventListener( - "focusin", - function (event: KeyboardEvent) { - self.navigator.view?.snap(event.target as HTMLElement); - }, - true - ); + element.addEventListener("focusin", this.onFocusIn(self), true); }; public keydown = (element: HTMLElement | Document): void => { const self = this; if (!this.navigator.rights.customKeyboardEvents) { - element.addEventListener( - "keydown", - function (event: KeyboardEvent) { - // Ignore input elements - const eventTarget = event.target as HTMLElement; - if (/input|select|option|textarea/i.test(eventTarget.tagName)) { - return; - } + element.addEventListener("keydown", this.onKeyDown(self), false); + } + }; - // Ignore when active text selection - const ownerDocument = (eventTarget.ownerDocument || - eventTarget) as HTMLDocument; - const ownerWindow = ownerDocument.defaultView as Window; - const selection = ownerWindow.getSelection() as Selection; - if (!selection.isCollapsed) { - return; - } + // store the generated event handlers, so they can be returned + // when removing the event listeners + private handlers = {}; - const key = event.key; - switch (key) { - case "ArrowRight": - self.onForwardSwipe(event); - break; - case "ArrowLeft": + private onFocusIn(self: this) { + return ( + this.handlers["onFocusIn"] || + (this.handlers["onFocusIn"] = function (event: KeyboardEvent) { + self.navigator.view?.snap(event.target as HTMLElement); + }) + ); + } + + private onKeyDown(self: this) { + return ( + this.handlers["onKeyDown"] || + (this.handlers["onKeyDown"] = function(event: KeyboardEvent) { + // Ignore input elements + const eventTarget = event.target as HTMLElement; + if (/input|select|option|textarea/i.test(eventTarget.tagName)) { + return; + } + + // Ignore when active text selection + const ownerDocument = (eventTarget.ownerDocument || + eventTarget) as HTMLDocument; + const ownerWindow = ownerDocument.defaultView as Window; + const selection = ownerWindow.getSelection() as Selection; + if (!selection.isCollapsed) { + return; + } + + const key = event.key; + switch (key) { + case "ArrowRight": + self.onForwardSwipe(event); + break; + case "ArrowLeft": + self.onBackwardSwipe(event); + break; + } + switch (event.code) { + case "Space": + if (event.ctrlKey) { self.onBackwardSwipe(event); - break; - } - switch (event.code) { - case "Space": - if (event.ctrlKey) { - self.onBackwardSwipe(event); - } else { - self.onForwardSwipe(event); - } - break; - } - }, - false - ); - } - }; + } else { + self.onForwardSwipe(event); + } + break; + } + }) + ); + } }