Skip to content

Commit 200d92d

Browse files
JiaLiPassionvicb
authored andcommitted
fix(core): should support event.stopImmediatePropagation (angular#19222)
1 parent dbec3ca commit 200d92d

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

packages/platform-browser/src/dom/events/dom_events.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const FALSE = 'FALSE';
3434
const ANGULAR = 'ANGULAR';
3535
const NATIVE_ADD_LISTENER = 'addEventListener';
3636
const NATIVE_REMOVE_LISTENER = 'removeEventListener';
37+
// use the same symbol string which is used in zone.js
38+
const stopSymbol = '__zone_symbol__propagationStopped';
3739

3840
const blackListedEvents: string[] =
3941
(typeof Zone !== 'undefined') && (Zone as any)[__symbol__('BLACK_LISTED_EVENTS')];
@@ -81,6 +83,9 @@ const globalListener = function(event: Event) {
8183
// itself or others
8284
const copiedTasks = taskDatas.slice();
8385
for (let i = 0; i < copiedTasks.length; i++) {
86+
if ((event as any)[stopSymbol] === true) {
87+
break;
88+
}
8489
const taskData = copiedTasks[i];
8590
if (taskData.zone !== Zone.current) {
8691
// only use Zone.run when Zone.current not equals to stored zone
@@ -94,7 +99,29 @@ const globalListener = function(event: Event) {
9499

95100
@Injectable()
96101
export class DomEventsPlugin extends EventManagerPlugin {
97-
constructor(@Inject(DOCUMENT) doc: any, private ngZone: NgZone) { super(doc); }
102+
constructor(@Inject(DOCUMENT) doc: any, private ngZone: NgZone) {
103+
super(doc);
104+
105+
this.patchEvent();
106+
}
107+
108+
private patchEvent() {
109+
if (!Event || !Event.prototype) {
110+
return;
111+
}
112+
const symbol = '__zone_symbol__stopImmediatePropagation';
113+
if ((Event.prototype as any)[symbol]) {
114+
// already patched by zone.js
115+
return;
116+
}
117+
(Event.prototype as any)[symbol] = Event.prototype.stopImmediatePropagation;
118+
Event.prototype.stopImmediatePropagation = function() {
119+
if (this) {
120+
this[stopSymbol] = true;
121+
}
122+
};
123+
}
124+
98125

99126
// This plugin should come last in the list of plugins, because it accepts all
100127
// events.

packages/platform-browser/test/dom/events/event_manager_spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,42 @@ export function main() {
152152
expect(receivedEvents).toEqual([]);
153153
});
154154

155+
it('should support event.stopImmediatePropagation', () => {
156+
const Zone = (window as any)['Zone'];
157+
158+
const element = el('<div><div></div></div>');
159+
getDOM().appendChild(doc.body, element);
160+
const dispatchedEvent = getDOM().createMouseEvent('click');
161+
let receivedEvents: any[] /** TODO #9100 */ = [];
162+
let receivedZones: any[] = [];
163+
const handler1 = (e: any /** TODO #9100 */) => {
164+
receivedEvents.push(e);
165+
receivedZones.push(Zone.current.name);
166+
e.stopImmediatePropagation();
167+
};
168+
const handler2 = (e: any /** TODO #9100 */) => {
169+
receivedEvents.push(e);
170+
receivedZones.push(Zone.current.name);
171+
};
172+
const manager = new EventManager([domEventPlugin], new FakeNgZone());
173+
174+
let remover1 = null;
175+
let remover2 = null;
176+
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
177+
Zone.root.fork({name: 'test'}).run(() => {
178+
remover2 = manager.addEventListener(element, 'click', handler2);
179+
});
180+
getDOM().dispatchEvent(element, dispatchedEvent);
181+
expect(receivedEvents).toEqual([dispatchedEvent]);
182+
expect(receivedZones).toEqual([Zone.root.name]);
183+
184+
receivedEvents = [];
185+
remover1 && remover1();
186+
remover2 && remover2();
187+
getDOM().dispatchEvent(element, dispatchedEvent);
188+
expect(receivedEvents).toEqual([]);
189+
});
190+
155191
it('should handle event correctly when one handler remove itself ', () => {
156192
const Zone = (window as any)['Zone'];
157193

0 commit comments

Comments
 (0)