66 * found in the LICENSE file at https://angular.io/license
77 */
88import { ElementRef , NgZone } from '@angular/core' ;
9- import { Platform , normalizePassiveListenerOptions } from '@angular/cdk/platform' ;
9+ import { Platform , normalizePassiveListenerOptions , _getEventTarget } from '@angular/cdk/platform' ;
1010import { isFakeMousedownFromScreenReader , isFakeTouchstartFromScreenReader } from '@angular/cdk/a11y' ;
1111import { coerceElement } from '@angular/cdk/coercion' ;
1212import { RippleRef , RippleState , RippleConfig } from './ripple-ref' ;
13+ import { RippleEventManager } from './ripple-event-manager' ;
1314
1415/**
1516 * Interface that describes the target for launching ripples.
@@ -45,8 +46,11 @@ export const defaultRippleAnimationConfig = {
4546 */
4647const ignoreMouseEventsTimeout = 800 ;
4748
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+ } ) ;
5054
5155/** Events that signal that the pointer is down. */
5256const pointerDownEvents = [ 'mousedown' , 'touchstart' ] ;
@@ -94,14 +98,16 @@ export class RippleRenderer implements EventListenerObject {
9498 */
9599 private _containerRect : ClientRect | null ;
96100
101+ private static _eventManager = new RippleEventManager ( ) ;
102+
97103 constructor (
98104 private _target : RippleTarget ,
99105 private _ngZone : NgZone ,
100106 elementOrElementRef : HTMLElement | ElementRef < HTMLElement > ,
101- platform : Platform ,
107+ private _platform : Platform ,
102108 ) {
103109 // Only do anything if we're on the browser.
104- if ( platform . isBrowser ) {
110+ if ( _platform . isBrowser ) {
105111 this . _containerElement = coerceElement ( elementOrElementRef ) ;
106112 }
107113 }
@@ -252,15 +258,19 @@ export class RippleRenderer implements EventListenerObject {
252258 setupTriggerEvents ( elementOrElementRef : HTMLElement | ElementRef < HTMLElement > ) {
253259 const element = coerceElement ( elementOrElementRef ) ;
254260
255- if ( ! element || element === this . _triggerElement ) {
261+ if ( ! this . _platform . isBrowser || ! element || element === this . _triggerElement ) {
256262 return ;
257263 }
258264
259265 // Remove all previously registered event listeners from the trigger element.
260266 this . _removeTriggerEvents ( ) ;
261-
262267 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+ } ) ;
264274 }
265275
266276 /**
@@ -280,7 +290,17 @@ export class RippleRenderer implements EventListenerObject {
280290 // We do this on-demand in order to reduce the total number of event listeners
281291 // registered by the ripples, which speeds up the rendering time for large UIs.
282292 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+
284304 this . _pointerUpEventsRegistered = true ;
285305 }
286306 }
@@ -393,30 +413,23 @@ export class RippleRenderer implements EventListenerObject {
393413 } ) ;
394414 }
395415
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-
405416 private _getActiveRipples ( ) : RippleRef [ ] {
406417 return Array . from ( this . _activeRipples . keys ( ) ) ;
407418 }
408419
409420 /** Removes previously registered event listeners from the trigger element. */
410421 _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+ ) ;
415428
416429 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+ ) ;
420433 }
421434 }
422435 }
0 commit comments