Skip to content

Commit 1fb0954

Browse files
lpincaBethGriggs
authored andcommitted
events: allow an event to be dispatched multiple times
Use a different flag to prevent recursive dispatching. PR-URL: #39395 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 6f2989c commit 1fb0954

File tree

2 files changed

+43
-5
lines changed

2 files changed

+43
-5
lines changed

lib/internal/event_target.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const {
4747
} = EventEmitter;
4848

4949
const kEvents = Symbol('kEvents');
50+
const kIsBeingDispatched = Symbol('kIsBeingDispatched');
5051
const kStop = Symbol('kStop');
5152
const kTarget = Symbol('kTarget');
5253
const kHandlers = Symbol('khandlers');
@@ -105,6 +106,7 @@ class Event {
105106
configurable: false
106107
});
107108
this[kTarget] = null;
109+
this[kIsBeingDispatched] = false;
108110
}
109111

110112
[customInspectSymbol](depth, options) {
@@ -151,12 +153,12 @@ class Event {
151153
// These are not supported in Node.js and are provided purely for
152154
// API completeness.
153155

154-
composedPath() { return this[kTarget] ? [this[kTarget]] : []; }
156+
composedPath() { return this[kIsBeingDispatched] ? [this[kTarget]] : []; }
155157
get returnValue() { return !this.defaultPrevented; }
156158
get bubbles() { return this[kBubbles]; }
157159
get composed() { return this[kComposed]; }
158160
get eventPhase() {
159-
return this[kTarget] ? Event.AT_TARGET : Event.NONE;
161+
return this[kIsBeingDispatched] ? Event.AT_TARGET : Event.NONE;
160162
}
161163
get cancelBubble() { return this[kPropagationStopped]; }
162164
set cancelBubble(value) {
@@ -397,7 +399,7 @@ class EventTarget {
397399
if (!isEventTarget(this))
398400
throw new ERR_INVALID_THIS('EventTarget');
399401

400-
if (event[kTarget] !== null)
402+
if (event[kIsBeingDispatched])
401403
throw new ERR_EVENT_RECURSION(event.type);
402404

403405
this[kHybridDispatch](event, event.type, event);
@@ -410,11 +412,14 @@ class EventTarget {
410412
if (event === undefined) {
411413
event = this[kCreateEvent](nodeValue, type);
412414
event[kTarget] = this;
415+
event[kIsBeingDispatched] = true;
413416
}
414417
return event;
415418
};
416-
if (event !== undefined)
419+
if (event !== undefined) {
417420
event[kTarget] = this;
421+
event[kIsBeingDispatched] = true;
422+
}
418423

419424
const root = this[kEvents].get(type);
420425
if (root === undefined || root.next === undefined)
@@ -453,6 +458,9 @@ class EventTarget {
453458
let result;
454459
if (callback) {
455460
result = FunctionPrototypeCall(callback, this, arg);
461+
if (!handler.isNodeStyleListener) {
462+
arg[kIsBeingDispatched] = false;
463+
}
456464
}
457465
if (result !== undefined && result !== null)
458466
addCatch(result);
@@ -464,7 +472,7 @@ class EventTarget {
464472
}
465473

466474
if (event !== undefined)
467-
event[kTarget] = undefined;
475+
event[kIsBeingDispatched] = false;
468476
}
469477

470478
[kCreateEvent](nodeValue, type) {

test/parallel/test-eventtarget.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,36 @@ let asyncTest = Promise.resolve();
162162
eventTarget.dispatchEvent(ev);
163163
}
164164

165+
{
166+
// Same event dispatched multiple times.
167+
const event = new Event('foo');
168+
const eventTarget1 = new EventTarget();
169+
const eventTarget2 = new EventTarget();
170+
171+
eventTarget1.addEventListener('foo', common.mustCall((event) => {
172+
strictEqual(event.eventPhase, Event.AT_TARGET);
173+
strictEqual(event.target, eventTarget1);
174+
deepStrictEqual(event.composedPath(), [eventTarget1]);
175+
}));
176+
177+
eventTarget2.addEventListener('foo', common.mustCall((event) => {
178+
strictEqual(event.eventPhase, Event.AT_TARGET);
179+
strictEqual(event.target, eventTarget2);
180+
deepStrictEqual(event.composedPath(), [eventTarget2]);
181+
}));
182+
183+
eventTarget1.dispatchEvent(event);
184+
strictEqual(event.eventPhase, Event.NONE);
185+
strictEqual(event.target, eventTarget1);
186+
deepStrictEqual(event.composedPath(), []);
187+
188+
189+
eventTarget2.dispatchEvent(event);
190+
strictEqual(event.eventPhase, Event.NONE);
191+
strictEqual(event.target, eventTarget2);
192+
deepStrictEqual(event.composedPath(), []);
193+
}
194+
165195
{
166196
const eventTarget = new EventTarget();
167197
const event = new Event('foo', { cancelable: true });

0 commit comments

Comments
 (0)