Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 98dd51f

Browse files
committed
fix(event): in IE, fix #1128, #589, pointer event will be transformed in IE
1 parent 31fc127 commit 98dd51f

File tree

3 files changed

+211
-8
lines changed

3 files changed

+211
-8
lines changed

file-size-limit.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"path": "dist/zone.min.js",
55
"checkTarget": true,
6-
"limit": 42050
6+
"limit": 45000
77
}
88
]
9-
}
9+
}

lib/common/events.ts

+63-6
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,26 @@
1010
* @suppress {missingRequire}
1111
*/
1212

13-
import {ADD_EVENT_LISTENER_STR, attachOriginToPatched, FALSE_STR, ObjectGetPrototypeOf, REMOVE_EVENT_LISTENER_STR, TRUE_STR, ZONE_SYMBOL_PREFIX, zoneSymbol} from './utils';
13+
import {ADD_EVENT_LISTENER_STR, attachOriginToPatched, FALSE_STR, isIE, isIEOrEdge, ObjectGetPrototypeOf, REMOVE_EVENT_LISTENER_STR, TRUE_STR, ZONE_SYMBOL_PREFIX, zoneSymbol} from './utils';
1414

1515
/** @internal **/
1616
interface EventTaskData extends TaskData {
1717
// use global callback or not
1818
readonly useG?: boolean;
1919
}
2020

21+
const pointerEventsMap: {[key: string]: string} = {
22+
'MSPointerCancel': 'pointercancel',
23+
'MSPointerDown': 'pointerdown',
24+
'MSPointerEnter': 'pointerenter',
25+
'MSPointerHover': 'pointerhover',
26+
'MSPointerLeave': 'pointerleave',
27+
'MSPointerMove': 'pointermove',
28+
'MSPointerOut': 'pointerout',
29+
'MSPointerOver': 'pointerover',
30+
'MSPointerUp': 'pointerup'
31+
};
32+
2133
let passiveSupported = false;
2234

2335
if (typeof window !== 'undefined') {
@@ -87,6 +99,16 @@ export function patchEventTarget(
8799
const PREPEND_EVENT_LISTENER = 'prependListener';
88100
const PREPEND_EVENT_LISTENER_SOURCE = '.' + PREPEND_EVENT_LISTENER + ':';
89101

102+
Object.keys(pointerEventsMap).forEach(msEventName => {
103+
const eventName = pointerEventsMap[msEventName];
104+
zoneSymbolEventNames[eventName] = {};
105+
zoneSymbolEventNames[eventName][FALSE_STR] = ZONE_SYMBOL_PREFIX + eventName + FALSE_STR;
106+
zoneSymbolEventNames[eventName][TRUE_STR] = ZONE_SYMBOL_PREFIX + eventName + TRUE_STR;
107+
zoneSymbolEventNames[msEventName] = {};
108+
zoneSymbolEventNames[msEventName][FALSE_STR] = ZONE_SYMBOL_PREFIX + msEventName + FALSE_STR;
109+
zoneSymbolEventNames[msEventName][TRUE_STR] = ZONE_SYMBOL_PREFIX + msEventName + TRUE_STR;
110+
});
111+
90112
const invokeTask = function(task: any, target: any, event: Event) {
91113
// for better performance, check isRemoved which is set
92114
// by removeEventListener
@@ -122,7 +144,15 @@ export function patchEventTarget(
122144
// event.target is needed for Samsung TV and SourceBuffer
123145
// || global is needed https://github.com/angular/zone.js/issues/190
124146
const target: any = this || event.target || _global;
125-
const tasks = target[zoneSymbolEventNames[event.type][FALSE_STR]];
147+
let tasks = target[zoneSymbolEventNames[event.type][FALSE_STR]];
148+
if (isIEOrEdge) {
149+
const pointerMappedEvent = pointerEventsMap[event.type];
150+
const msTasks =
151+
pointerMappedEvent && target[zoneSymbolEventNames[pointerMappedEvent]][FALSE_STR];
152+
if (msTasks) {
153+
tasks = tasks.concat(msTasks);
154+
}
155+
}
126156
if (tasks) {
127157
// invoke all tasks which attached to current target with given event.type and capture = false
128158
// for performance concern, if task.length === 1, just invoke
@@ -154,7 +184,15 @@ export function patchEventTarget(
154184
// event.target is needed for Samsung TV and SourceBuffer
155185
// || global is needed https://github.com/angular/zone.js/issues/190
156186
const target: any = this || event.target || _global;
157-
const tasks = target[zoneSymbolEventNames[event.type][TRUE_STR]];
187+
let tasks = target[zoneSymbolEventNames[event.type][TRUE_STR]];
188+
if (isIEOrEdge) {
189+
const pointerMappedEvent = pointerEventsMap[event.type];
190+
const msTasks =
191+
pointerMappedEvent && target[zoneSymbolEventNames[pointerMappedEvent]][FALSE_STR];
192+
if (msTasks) {
193+
tasks = tasks.concat(msTasks);
194+
}
195+
}
158196
if (tasks) {
159197
// invoke all tasks which attached to current target with given event.type and capture = false
160198
// for performance concern, if task.length === 1, just invoke
@@ -350,7 +388,11 @@ export function patchEventTarget(
350388
return;
351389
}
352390

353-
const eventName = arguments[0];
391+
let eventName = arguments[0];
392+
if (isIEOrEdge) {
393+
const msEventName = pointerEventsMap[eventName];
394+
eventName = msEventName ? msEventName : eventName;
395+
}
354396
const options = arguments[2];
355397

356398
if (blackListedEvents) {
@@ -393,6 +435,13 @@ export function patchEventTarget(
393435
}
394436
let existingTasks = target[symbolEventName];
395437
let isExisting = false;
438+
if (isIEOrEdge && !existingTasks) {
439+
const msEventName = pointerEventsMap[eventName];
440+
if (msEventName) {
441+
existingTasks =
442+
target[zoneSymbolEventNames[msEventName][capture ? TRUE_STR : FALSE_STR]];
443+
}
444+
}
396445
if (existingTasks) {
397446
// already have task registered
398447
isExisting = true;
@@ -427,7 +476,8 @@ export function patchEventTarget(
427476
}
428477
taskData.target = target;
429478
taskData.capture = capture;
430-
taskData.eventName = eventName;
479+
// in pointer event, we need to use original event name here.
480+
taskData.eventName = arguments[0];
431481
taskData.isExisting = isExisting;
432482

433483
const data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : undefined;
@@ -489,7 +539,11 @@ export function patchEventTarget(
489539

490540
proto[REMOVE_EVENT_LISTENER] = function() {
491541
const target = this || _global;
492-
const eventName = arguments[0];
542+
let eventName = arguments[0];
543+
if (isIEOrEdge) {
544+
const msEventName = pointerEventsMap[eventName];
545+
eventName = msEventName ? msEventName : eventName;
546+
}
493547
const options = arguments[2];
494548

495549
let capture;
@@ -531,6 +585,9 @@ export function patchEventTarget(
531585
// remove globalZoneAwareCallback and remove the task cache from target
532586
(existingTask as any).allRemoved = true;
533587
target[symbolEventName] = null;
588+
if (isIEOrEdge) {
589+
existingTask.eventName = arguments[0];
590+
}
534591
}
535592
existingTask.zone.cancelTask(existingTask);
536593
if (returnTarget) {

test/browser/browser.spec.ts

+146
Original file line numberDiff line numberDiff line change
@@ -2662,5 +2662,151 @@ describe('Zone', function() {
26622662
});
26632663
}));
26642664
});
2665+
2666+
describe(
2667+
'pointer event in IE',
2668+
ifEnvSupports(
2669+
() => {
2670+
return getIEVersion() === 11;
2671+
},
2672+
() => {
2673+
const pointerEventsMap: {[key: string]: string} = {
2674+
'MSPointerCancel': 'pointercancel',
2675+
'MSPointerDown': 'pointerdown',
2676+
'MSPointerEnter': 'pointerenter',
2677+
'MSPointerHover': 'pointerhover',
2678+
'MSPointerLeave': 'pointerleave',
2679+
'MSPointerMove': 'pointermove',
2680+
'MSPointerOut': 'pointerout',
2681+
'MSPointerOver': 'pointerover',
2682+
'MSPointerUp': 'pointerup'
2683+
};
2684+
2685+
let div: HTMLDivElement;
2686+
beforeEach(() => {
2687+
div = document.createElement('div');
2688+
document.body.appendChild(div);
2689+
});
2690+
afterEach(() => {
2691+
document.body.removeChild(div);
2692+
});
2693+
Object.keys(pointerEventsMap).forEach(key => {
2694+
it(`${key} and ${pointerEventsMap[key]} should both be triggered`,
2695+
(done: DoneFn) => {
2696+
const logs: string[] = [];
2697+
div.addEventListener(key, (event: any) => {
2698+
expect(event.type).toEqual(pointerEventsMap[key]);
2699+
logs.push(`${key} triggered`);
2700+
});
2701+
div.addEventListener(pointerEventsMap[key], (event: any) => {
2702+
expect(event.type).toEqual(pointerEventsMap[key]);
2703+
logs.push(`${pointerEventsMap[key]} triggered`);
2704+
});
2705+
const evt1 = document.createEvent('Event');
2706+
evt1.initEvent(key, true, true);
2707+
div.dispatchEvent(evt1);
2708+
2709+
setTimeout(() => {
2710+
expect(logs).toEqual(
2711+
[`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
2712+
});
2713+
2714+
const evt2 = document.createEvent('Event');
2715+
evt2.initEvent(pointerEventsMap[key], true, true);
2716+
div.dispatchEvent(evt2);
2717+
2718+
setTimeout(() => {
2719+
expect(logs).toEqual(
2720+
[`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
2721+
});
2722+
2723+
setTimeout(done);
2724+
});
2725+
2726+
it(`${key} and ${
2727+
pointerEventsMap[key]} with same listener should not be triggered twice`,
2728+
(done: DoneFn) => {
2729+
const logs: string[] = [];
2730+
const listener = function(event: any) {
2731+
expect(event.type).toEqual(pointerEventsMap[key]);
2732+
logs.push(`${key} triggered`);
2733+
};
2734+
div.addEventListener(key, listener);
2735+
div.addEventListener(pointerEventsMap[key], listener);
2736+
2737+
const evt1 = document.createEvent('Event');
2738+
evt1.initEvent(key, true, true);
2739+
div.dispatchEvent(evt1);
2740+
2741+
setTimeout(() => {
2742+
expect(logs).toEqual([`${key} triggered`]);
2743+
});
2744+
2745+
const evt2 = document.createEvent('Event');
2746+
evt2.initEvent(pointerEventsMap[key], true, true);
2747+
div.dispatchEvent(evt2);
2748+
2749+
setTimeout(() => {
2750+
expect(logs).toEqual([`${pointerEventsMap[key]} triggered`]);
2751+
});
2752+
2753+
setTimeout(done);
2754+
});
2755+
2756+
it(`${key} and ${
2757+
pointerEventsMap
2758+
[key]} should be able to be removed with removeEventListener`,
2759+
(done: DoneFn) => {
2760+
const logs: string[] = [];
2761+
const listener1 = function(event: any) {
2762+
logs.push(`${key} triggered`);
2763+
};
2764+
const listener2 = function(event: any) {
2765+
logs.push(`${pointerEventsMap[key]} triggered`);
2766+
};
2767+
div.addEventListener(key, listener1);
2768+
div.addEventListener(pointerEventsMap[key], listener2);
2769+
2770+
div.removeEventListener(key, listener1);
2771+
div.removeEventListener(key, listener2);
2772+
2773+
const evt1 = document.createEvent('Event');
2774+
evt1.initEvent(key, true, true);
2775+
div.dispatchEvent(evt1);
2776+
2777+
setTimeout(() => {
2778+
expect(logs).toEqual([]);
2779+
});
2780+
2781+
const evt2 = document.createEvent('Event');
2782+
evt2.initEvent(pointerEventsMap[key], true, true);
2783+
div.dispatchEvent(evt2);
2784+
2785+
setTimeout(() => {
2786+
expect(logs).toEqual([]);
2787+
});
2788+
2789+
div.addEventListener(key, listener1);
2790+
div.addEventListener(pointerEventsMap[key], listener2);
2791+
2792+
div.removeEventListener(pointerEventsMap[key], listener1);
2793+
div.removeEventListener(pointerEventsMap[key], listener2);
2794+
2795+
div.dispatchEvent(evt1);
2796+
2797+
setTimeout(() => {
2798+
expect(logs).toEqual([]);
2799+
});
2800+
2801+
div.dispatchEvent(evt2);
2802+
2803+
setTimeout(() => {
2804+
expect(logs).toEqual([]);
2805+
});
2806+
2807+
setTimeout(done);
2808+
});
2809+
});
2810+
}));
26652811
});
26662812
});

0 commit comments

Comments
 (0)