From dd36787b8b29aca23d51c429388fa15af1e89702 Mon Sep 17 00:00:00 2001 From: louis cuny Date: Thu, 3 Nov 2022 14:59:29 +0100 Subject: [PATCH] Fix onDelegate used with nested targets --- src/index.test.ts | 33 +++++++++++++++------------------ src/index.ts | 25 ++++++++++++++++++------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 3e4c84c..9dbbeb4 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -11,7 +11,7 @@ describe("query utilities", () => {
- +
@@ -212,27 +212,24 @@ describe("query utilities", () => { describe(".query .onDelegate", () => { it("should call the callbacks on clicks with the correct child target", () => { - const container = query("#el1"); + const container = queryStrict("#el1"); const child1Id = "el2"; const child3Id = "el4"; - const child1 = query(`#${child1Id}`); - const child3 = query(`#${child3Id}`); - - expect(container).not.toBeNull(); - expect(child1).not.toBeNull(); - expect(child3).not.toBeNull(); - - if (container && child1 && child3) { - const mockCallback = jest.fn(); - container.onDelegate("span", "click", mockCallback); - child1.click(); - child3.click(); - expect(mockCallback).toHaveBeenCalledTimes(2); - expect(mockCallback.mock.calls[0][0].target.id).toBe(child1Id); - expect(mockCallback.mock.calls[1][0].target.id).toBe(child3Id); - } + const child1 = queryStrict(`#${child1Id}`); + const child3 = queryStrict(`#${child3Id}`); + const child3Child = child3.queryStrict("strong"); + + const mockCallback = jest.fn(); + container.onDelegate("span", "click", mockCallback); + child1.click(); + child3.click(); + child3Child.click(); + expect(mockCallback).toHaveBeenCalledTimes(3); + expect(mockCallback.mock.calls[0][0].currentTarget.id).toBe(child1Id); + expect(mockCallback.mock.calls[1][0].currentTarget.id).toBe(child3Id); + expect(mockCallback.mock.calls[2][0].currentTarget.id).toBe(child3Id); }); describe("should have the right even type", () => { diff --git a/src/index.ts b/src/index.ts index e66612e..d9c4488 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,14 @@ export interface EnhancedHTMLElement { queryAll: typeof queryAll; } +const overrideEventCurrentTarget = (event: Event, target: Element) => { + Object.defineProperty(event, "currentTarget", { + configurable: true, + enumerable: true, + get: () => target, + }); +}; + const enhancedHTMLElementImpl: EnhancedHTMLElement = { isEnhancedHTMLElement: true, on(type, callback, options) { @@ -38,12 +46,15 @@ const enhancedHTMLElementImpl: EnhancedHTMLElement = { }, onDelegate(childSelector, type, callback, options) { const containerListenerCallback = (e: Event) => { - if (e && e.target && (e.target as HTMLElement).matches(childSelector)) { - callback.call(e.target, e as WindowEventMap[typeof type]); - if (options && options.once) { - // to mimic the once configuration of the native option, which is not well supported (IE 11) - this.removeEventListener(type, containerListenerCallback); - } + if (!(e.target instanceof Element)) return; + + const target = e.target.closest(childSelector); + + if (!this.contains(target)) return; + + if (target) { + overrideEventCurrentTarget(e, target); + callback.call(target, e as WindowEventMap[typeof type]); } }; @@ -51,7 +62,7 @@ const enhancedHTMLElementImpl: EnhancedHTMLElement = { this.removeEventListener(type, containerListenerCallback); }; - this.addEventListener(type, containerListenerCallback); + this.addEventListener(type, containerListenerCallback, options); return removeListener; },