6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
import { ElementRef , NgZone } from '@angular/core' ;
9
- import { Platform , normalizePassiveListenerOptions } from '@angular/cdk/platform' ;
9
+ import { Platform , normalizePassiveListenerOptions , _getEventTarget } from '@angular/cdk/platform' ;
10
10
import { isFakeMousedownFromScreenReader , isFakeTouchstartFromScreenReader } from '@angular/cdk/a11y' ;
11
11
import { coerceElement } from '@angular/cdk/coercion' ;
12
12
import { RippleRef , RippleState , RippleConfig } from './ripple-ref' ;
13
+ import { RippleEventManager } from './ripple-event-manager' ;
13
14
14
15
/**
15
16
* Interface that describes the target for launching ripples.
@@ -45,8 +46,11 @@ export const defaultRippleAnimationConfig = {
45
46
*/
46
47
const ignoreMouseEventsTimeout = 800 ;
47
48
48
- /** Options that apply to all the event listeners that are bound by the ripple renderer. */
49
- const passiveEventOptions = normalizePassiveListenerOptions ( { passive : true } ) ;
49
+ /** Options used to bind a passive capturing event. */
50
+ const passiveCapturingEventOptions = normalizePassiveListenerOptions ( {
51
+ passive : true ,
52
+ capture : true ,
53
+ } ) ;
50
54
51
55
/** Events that signal that the pointer is down. */
52
56
const pointerDownEvents = [ 'mousedown' , 'touchstart' ] ;
@@ -94,14 +98,16 @@ export class RippleRenderer implements EventListenerObject {
94
98
*/
95
99
private _containerRect : ClientRect | null ;
96
100
101
+ private static _eventManager = new RippleEventManager ( ) ;
102
+
97
103
constructor (
98
104
private _target : RippleTarget ,
99
105
private _ngZone : NgZone ,
100
106
elementOrElementRef : HTMLElement | ElementRef < HTMLElement > ,
101
- platform : Platform ,
107
+ private _platform : Platform ,
102
108
) {
103
109
// Only do anything if we're on the browser.
104
- if ( platform . isBrowser ) {
110
+ if ( _platform . isBrowser ) {
105
111
this . _containerElement = coerceElement ( elementOrElementRef ) ;
106
112
}
107
113
}
@@ -252,15 +258,19 @@ export class RippleRenderer implements EventListenerObject {
252
258
setupTriggerEvents ( elementOrElementRef : HTMLElement | ElementRef < HTMLElement > ) {
253
259
const element = coerceElement ( elementOrElementRef ) ;
254
260
255
- if ( ! element || element === this . _triggerElement ) {
261
+ if ( ! this . _platform . isBrowser || ! element || element === this . _triggerElement ) {
256
262
return ;
257
263
}
258
264
259
265
// Remove all previously registered event listeners from the trigger element.
260
266
this . _removeTriggerEvents ( ) ;
261
-
262
267
this . _triggerElement = element ;
263
- this . _registerEvents ( pointerDownEvents ) ;
268
+
269
+ // Use event delegation for the trigger events since they're
270
+ // set up during creation and are performance-sensitive.
271
+ pointerDownEvents . forEach ( type => {
272
+ RippleRenderer . _eventManager . addHandler ( this . _ngZone , type , element , this ) ;
273
+ } ) ;
264
274
}
265
275
266
276
/**
@@ -280,7 +290,17 @@ export class RippleRenderer implements EventListenerObject {
280
290
// We do this on-demand in order to reduce the total number of event listeners
281
291
// registered by the ripples, which speeds up the rendering time for large UIs.
282
292
if ( ! this . _pointerUpEventsRegistered ) {
283
- this . _registerEvents ( pointerUpEvents ) ;
293
+ // The events for hiding the ripple are bound directly on the trigger, because:
294
+ // 1. Some of them occur frequently (e.g. `mouseleave`) and any advantage we get from
295
+ // delegation will be diminished by having to look through all the data structures often.
296
+ // 2. They aren't as performance-sensitive, because they're bound only after the user
297
+ // has interacted with an element.
298
+ this . _ngZone . runOutsideAngular ( ( ) => {
299
+ pointerUpEvents . forEach ( type => {
300
+ this . _triggerElement ! . addEventListener ( type , this , passiveCapturingEventOptions ) ;
301
+ } ) ;
302
+ } ) ;
303
+
284
304
this . _pointerUpEventsRegistered = true ;
285
305
}
286
306
}
@@ -393,30 +413,23 @@ export class RippleRenderer implements EventListenerObject {
393
413
} ) ;
394
414
}
395
415
396
- /** Registers event listeners for a given list of events. */
397
- private _registerEvents ( eventTypes : string [ ] ) {
398
- this . _ngZone . runOutsideAngular ( ( ) => {
399
- eventTypes . forEach ( type => {
400
- this . _triggerElement ! . addEventListener ( type , this , passiveEventOptions ) ;
401
- } ) ;
402
- } ) ;
403
- }
404
-
405
416
private _getActiveRipples ( ) : RippleRef [ ] {
406
417
return Array . from ( this . _activeRipples . keys ( ) ) ;
407
418
}
408
419
409
420
/** Removes previously registered event listeners from the trigger element. */
410
421
_removeTriggerEvents ( ) {
411
- if ( this . _triggerElement ) {
412
- pointerDownEvents . forEach ( type => {
413
- this . _triggerElement ! . removeEventListener ( type , this , passiveEventOptions ) ;
414
- } ) ;
422
+ const trigger = this . _triggerElement ;
423
+
424
+ if ( trigger ) {
425
+ pointerDownEvents . forEach ( type =>
426
+ RippleRenderer . _eventManager . removeHandler ( type , trigger , this ) ,
427
+ ) ;
415
428
416
429
if ( this . _pointerUpEventsRegistered ) {
417
- pointerUpEvents . forEach ( type => {
418
- this . _triggerElement ! . removeEventListener ( type , this , passiveEventOptions ) ;
419
- } ) ;
430
+ pointerUpEvents . forEach ( type =>
431
+ trigger . removeEventListener ( type , this , passiveCapturingEventOptions ) ,
432
+ ) ;
420
433
}
421
434
}
422
435
}
0 commit comments