@@ -69,29 +69,15 @@ interface InternalEventTask extends EventTask {
69
69
// its invocation if dispatched later.
70
70
isRemoved ?: boolean ;
71
71
allRemoved ?: boolean ;
72
- }
73
-
74
- // Note that passive event listeners are now supported by most modern browsers,
75
- // including Chrome, Firefox, Safari, and Edge. There's a pending change that
76
- // would remove support for legacy browsers by zone.js. Removing `passiveSupported`
77
- // from the codebase will reduce the final code size for existing apps that still use zone.js.
78
- let passiveSupported = false ;
79
-
80
- if ( typeof window !== 'undefined' ) {
81
- try {
82
- const options = Object . defineProperty ( { } , 'passive' , {
83
- get : function ( ) {
84
- passiveSupported = true ;
85
- } ,
86
- } ) ;
87
- // Note: We pass the `options` object as the event handler too. This is not compatible with the
88
- // signature of `addEventListener` or `removeEventListener` but enables us to remove the handler
89
- // without an actual handler.
90
- window . addEventListener ( 'test' , options as any , options ) ;
91
- window . removeEventListener ( 'test' , options as any , options ) ;
92
- } catch ( err ) {
93
- passiveSupported = false ;
94
- }
72
+ // `originalDelegate` is the actual event listener object passed when
73
+ // calling `addEventListener()`, i.e., `{ handleEvent: event => ... }`.
74
+ // This object is used to compare event listeners when `addEventListener`
75
+ // is called again with the same event listener object reference.
76
+ // For example:
77
+ // const eventListenerObject = { handleEvent: console.log };
78
+ // document.addEventListener('click', eventListenerObject);
79
+ // document.addEventListener('click', eventListenerObject);
80
+ originalDelegate ?: EventListenerObject ;
95
81
}
96
82
97
83
// an identifier to tell ZoneTask do not create a new invoke closure
@@ -116,31 +102,92 @@ function prepareEventNames(eventName: string, eventNameToString?: (eventName: st
116
102
}
117
103
118
104
export interface PatchEventTargetOptions {
119
- // validateHandler
105
+ /**
106
+ * Optional validator for the event handler before patching.
107
+ * If it returns false, the handler will not be patched.
108
+ *
109
+ * @param nativeDelegate The native method (e.g., original addEventListener).
110
+ * @param delegate The provided handler function.
111
+ * @param target The object being patched.
112
+ * @param args The arguments passed to the method.
113
+ * @returns Whether the handler is valid for patching.
114
+ */
120
115
vh ?: ( nativeDelegate : any , delegate : any , target : any , args : any ) => boolean ;
121
- // addEventListener function name
116
+
117
+ /**
118
+ * The property name for the method that adds an event listener.
119
+ * Typically `addEventListener`.
120
+ */
122
121
add ?: string ;
123
- // removeEventListener function name
122
+
123
+ /**
124
+ * The property name for the method that removes an event listener.
125
+ * Typically `removeEventListener`.
126
+ */
124
127
rm ?: string ;
125
- // prependEventListener function name
128
+
129
+ /**
130
+ * The property name for a method that prepends an event listener.
131
+ * Used in some Node.js-style APIs.
132
+ */
126
133
prepend ?: string ;
127
- // listeners function name
134
+
135
+ /**
136
+ * The property name for the method that returns the current listeners.
137
+ * `eventListeners` is the default.
138
+ *
139
+ * Example:
140
+ * ```js
141
+ * const element = document.querySelector(...);
142
+ * console.log(element.eventListeners());
143
+ * ```
144
+ */
128
145
listeners ?: string ;
129
- // removeAllListeners function name
146
+
147
+ /**
148
+ * The property name for the method that removes all listeners for an event.
149
+ * `removeAllListeners` is the default.
150
+ */
130
151
rmAll ?: string ;
131
- // useGlobalCallback flag
152
+
153
+ /**
154
+ * Indicates whether a shared global callback should be used for all events
155
+ * instead of individual per-event callbacks.
156
+ */
132
157
useG ?: boolean ;
133
- // check duplicate flag when addEventListener
158
+
159
+ /**
160
+ * If true, checks for duplicate listeners before adding a new one.
161
+ * Prevents multiple registrations of the same handler.
162
+ */
134
163
chkDup ?: boolean ;
135
- // return target flag when addEventListener
164
+
165
+ /**
166
+ * If true, the patched add method will return the target object
167
+ * (matching typical `addEventListener` behavior).
168
+ */
136
169
rt ?: boolean ;
137
- // event compare handler
170
+
171
+ /**
172
+ * Optional function to compare existing tasks with a given delegate.
173
+ * Used to match handlers when removing or managing listeners.
174
+ *
175
+ * @param task The internal Zone.js task object.
176
+ * @param delegate The original event handler function.
177
+ * @returns Whether the two refer to the same handler.
178
+ */
138
179
diff ?: ( task : any , delegate : any ) => boolean ;
139
- // support passive or not
140
- supportPassive ?: boolean ;
141
- // get string from eventName (in nodejs, eventName maybe Symbol)
180
+
181
+ /**
182
+ * Converts an event name to a string.
183
+ * Useful when event names are symbols (e.g., in Node.js).
184
+ */
142
185
eventNameToString ?: ( eventName : any ) => string ;
143
- // transfer eventName
186
+
187
+ /**
188
+ * Transforms or normalizes the event name before use.
189
+ * Allows remapping or renaming of event types.
190
+ */
144
191
transferEventName ?: ( eventName : string ) => string ;
145
192
}
146
193
@@ -322,13 +369,7 @@ export function patchEventTarget(
322
369
* to handle all possible input from the user.
323
370
*/
324
371
function buildEventListenerOptions ( options : any , passive : boolean ) {
325
- if ( ! passiveSupported && typeof options === 'object' && options ) {
326
- // doesn't support passive but user want to pass an object as options.
327
- // this will not work on some old browser, so we just pass a boolean
328
- // as useCapture parameter
329
- return ! ! options . capture ;
330
- }
331
- if ( ! passiveSupported || ! passive ) {
372
+ if ( ! passive ) {
332
373
return options ;
333
374
}
334
375
if ( typeof options === 'boolean' ) {
@@ -443,8 +484,7 @@ export function patchEventTarget(
443
484
) ;
444
485
} ;
445
486
446
- const compare =
447
- patchOptions && patchOptions . diff ? patchOptions . diff : compareTaskCallbackVsDelegate ;
487
+ const compare = patchOptions ?. diff || compareTaskCallbackVsDelegate ;
448
488
449
489
const unpatchedEvents : string [ ] = ( Zone as any ) [ zoneSymbol ( 'UNPATCHED_EVENTS' ) ] ;
450
490
const passiveEvents : string [ ] = _global [ zoneSymbol ( 'PASSIVE_EVENTS' ) ] ;
@@ -487,7 +527,7 @@ export function patchEventTarget(
487
527
if ( patchOptions && patchOptions . transferEventName ) {
488
528
eventName = patchOptions . transferEventName ( eventName ) ;
489
529
}
490
- let delegate = arguments [ 1 ] ;
530
+ let delegate : EventListenerOrEventListenerObject = arguments [ 1 ] ;
491
531
if ( ! delegate ) {
492
532
return nativeListener . apply ( this , arguments ) ;
493
533
}
@@ -496,23 +536,24 @@ export function patchEventTarget(
496
536
return nativeListener . apply ( this , arguments ) ;
497
537
}
498
538
499
- // don't create the bind delegate function for handleEvent
500
- // case here to improve addEventListener performance
501
- // we will create the bind delegate when invoke
502
- let isHandleEvent = false ;
539
+ // To improve `addEventListener` performance, we will create the callback
540
+ // for the task later when the task is invoked.
541
+ let isEventListenerObject = false ;
503
542
if ( typeof delegate !== 'function' ) {
543
+ // This checks whether the provided listener argument is an object with
544
+ // a `handleEvent` method (since we can call `addEventListener` with a
545
+ // function `event => ...` or with an object `{ handleEvent: event => ... }`).
504
546
if ( ! delegate . handleEvent ) {
505
547
return nativeListener . apply ( this , arguments ) ;
506
548
}
507
- isHandleEvent = true ;
549
+ isEventListenerObject = true ;
508
550
}
509
551
510
552
if ( validateHandler && ! validateHandler ( nativeListener , delegate , target , arguments ) ) {
511
553
return ;
512
554
}
513
555
514
- const passive =
515
- passiveSupported && ! ! passiveEvents && passiveEvents . indexOf ( eventName ) !== - 1 ;
556
+ const passive = ! ! passiveEvents && passiveEvents . indexOf ( eventName ) !== - 1 ;
516
557
const options = copyEventListenerOptions ( buildEventListenerOptions ( arguments [ 2 ] , passive ) ) ;
517
558
const signal : AbortSignal | undefined = options ?. signal ;
518
559
if ( signal ?. aborted ) {
@@ -610,7 +651,7 @@ export function patchEventTarget(
610
651
// `taskData.options` to pass it to the native `addEventListener`.
611
652
const task : InternalEventTask = zone . scheduleEventTask (
612
653
source ,
613
- delegate ,
654
+ < Function > delegate ,
614
655
data ,
615
656
customScheduleFn ,
616
657
customCancelFn ,
@@ -645,17 +686,18 @@ export function patchEventTarget(
645
686
if ( once ) {
646
687
taskData . options . once = true ;
647
688
}
648
- if ( ! ( ! passiveSupported && typeof task . options === 'boolean' ) ) {
649
- // if not support passive, and we pass an option object
650
- // to addEventListener, we should save the options to task
689
+ if ( typeof task . options !== 'boolean' ) {
690
+ // We should save the options on the task (if it's an object) because
691
+ // we'll be using `task.options` later when removing the event listener
692
+ // and passing it back to `removeEventListener`.
651
693
task . options = options ;
652
694
}
653
695
task . target = target ;
654
696
task . capture = capture ;
655
697
task . eventName = eventName ;
656
- if ( isHandleEvent ) {
698
+ if ( isEventListenerObject ) {
657
699
// save original delegate for compare to check duplicate
658
- ( task as any ) . originalDelegate = delegate ;
700
+ task . originalDelegate = < EventListenerObject > delegate ;
659
701
}
660
702
if ( ! prepend ) {
661
703
existingTasks . push ( task ) ;
0 commit comments