diff --git a/src/runtime/blockdom/events.ts b/src/runtime/blockdom/events.ts index 9a6c73382..54f94348e 100644 --- a/src/runtime/blockdom/events.ts +++ b/src/runtime/blockdom/events.ts @@ -55,6 +55,11 @@ function createElementHandler(evName: string, capture: boolean = false): EventHa // listener per event type. let nextSyntheticEventId = 1; function createSyntheticHandler(evName: string, capture: boolean = false): EventHandlerCreator { + if (NON_BUBBLING_EVENT_NAMES.has(evName)) { + throw new Error( + `cannot set synthetic event handler for "${evName}" events: these events do not bubble by default` + ); + } let eventKey = `__event__synthetic_${evName}`; if (capture) { eventKey = `${eventKey}_capture`; @@ -88,14 +93,27 @@ function nativeToSyntheticEvent(eventKey: string, event: Event) { } } -const CONFIGURED_SYNTHETIC_EVENTS: { [event: string]: boolean } = {}; +const NON_BUBBLING_EVENT_NAMES = new Set([ + "blur", + "error", + "focus", + "hashchange", + "load", + "mouseenter", + "mouseleave", + "pointercancel", + "pointerenter", + "pointerleave", + "unload", +]); function setupSyntheticEvent(evName: string, eventKey: string, capture: boolean = false) { - if (CONFIGURED_SYNTHETIC_EVENTS[eventKey]) { + const root = document as any; + if (root[eventKey]) { return; } - document.addEventListener(evName, (event) => nativeToSyntheticEvent(eventKey, event), { - capture, - }); - CONFIGURED_SYNTHETIC_EVENTS[eventKey] = true; + const syntheticHandler = nativeToSyntheticEvent.bind(root, eventKey); + const options = { capture }; + root[eventKey] = [evName, syntheticHandler, options]; + root.addEventListener(evName, syntheticHandler, options); } diff --git a/tests/compiler/event_handling.test.ts b/tests/compiler/event_handling.test.ts index 11b98738b..82e9b4cd1 100644 --- a/tests/compiler/event_handling.test.ts +++ b/tests/compiler/event_handling.test.ts @@ -575,5 +575,16 @@ describe("t-on", () => { button.click(); expect(steps).toEqual(["btnClicked", "divClicked"]); }); + + test("non-bubbling events cannot be synthesized", () => { + const template = `
`; + + const owner = { + onMouseEnter() {}, + }; + const node = mountToFixture(template, owner); + const div =