-
Notifications
You must be signed in to change notification settings - Fork 100
Event bubbling keep target #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d7f97a
e3edd51
cd7ee5c
6ad4834
8b0eaa2
12b2814
2e49e2c
dfde93e
5e81da8
edc9d2d
fca1bd3
e02a9c6
2560e60
b62936f
9b39dd7
c989cff
d634532
58712cf
63e1f5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,50 @@ | ||
| 'use strict'; | ||
| // https://dom.spec.whatwg.org/#interface-eventtarget | ||
|
|
||
| const EventTarget = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('@ungap/event-target')); | ||
| const wm = new WeakMap(); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of extending Node or ungap EventTarget all features are now here. |
||
|
|
||
| function dispatch({ target, listener}) { | ||
| if (typeof listener === 'function') { | ||
| listener.call(target, this); | ||
| } else { | ||
| listener.handleEvent(this); | ||
| } | ||
| return this._stopImmediatePropagationFlag; | ||
| } | ||
|
|
||
| function invokeListeners({currentTarget, target}) { | ||
| const map = wm.get(currentTarget); | ||
| if (map && map.has(this.type)) { | ||
| const listeners = map.get(this.type); | ||
| if (currentTarget === target) { | ||
| this.eventPhase = this.AT_TARGET; | ||
| } else { | ||
| this.eventPhase = this.BUBBLING_PHASE; | ||
| } | ||
|
|
||
| this.currentTarget = currentTarget; | ||
| this.target = target; | ||
| for (const [listener, options] of listeners) { | ||
| if (options && options.once) | ||
| listeners.delete(listener); | ||
| if (dispatch.call(this, {target: this, listener})) | ||
| break; | ||
| } | ||
| delete this.currentTarget; | ||
| delete this.target; | ||
| return this.cancelBubble; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * @implements globalThis.EventTarget | ||
| */ | ||
| class DOMEventTarget extends EventTarget { | ||
| class DOMEventTarget { | ||
|
|
||
| constructor() { | ||
| wm.set(this, new Map); | ||
| } | ||
|
|
||
| /** | ||
| * @protected | ||
|
|
@@ -15,24 +53,39 @@ class DOMEventTarget extends EventTarget { | |
| return null; | ||
| } | ||
|
|
||
| addEventListener(type, listener, options) { | ||
| const map = wm.get(this); | ||
| if (!map.has(type)) | ||
| map.set(type, new Map); | ||
| map.get(type).set(listener, options); | ||
| } | ||
|
|
||
| removeEventListener(type, listener) { | ||
| const map = wm.get(this); | ||
| if (map.has(type)) { | ||
| const listeners = map.get(type); | ||
| if (listeners.delete(listener) && !listeners.size) { | ||
| map.delete(type); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| dispatchEvent(event) { | ||
| const dispatched = super.dispatchEvent(event); | ||
| let node = this; | ||
| event.eventPhase = event.CAPTURING_PHASE; | ||
|
|
||
| // intentionally simplified, specs imply way more code: https://dom.spec.whatwg.org/#event-path | ||
| if (dispatched && event.bubbles && !event.cancelBubble) { | ||
| const parent = this._getParent(); | ||
| if (parent && parent.dispatchEvent) { | ||
| const options = { | ||
| bubbles: event.bubbles, | ||
| cancelable: event.cancelable, | ||
| composed: event.composed, | ||
| }; | ||
| // in Node 16.5 the same event can't be used for another dispatch | ||
| return parent.dispatchEvent(new event.constructor(event.type, options)); | ||
| } | ||
| while (node) { | ||
| if (node.dispatchEvent) | ||
| event._path.push({currentTarget: node, target: this}); | ||
| node = event.bubbles && node._getParent && node._getParent(); | ||
| } | ||
| return dispatched; | ||
| event._path.some(invokeListeners, event); | ||
| event._path = []; | ||
| event.eventPhase = event.NONE; | ||
| return !event.defaultPrevented; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. initially it was just returning true, but spec says
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| exports.EventTarget = DOMEventTarget; | ||
Uh oh!
There was an error while loading. Please reload this page.