Skip to content

Commit

Permalink
perf(ripple): use passive event listeners (#8719)
Browse files Browse the repository at this point in the history
Switches all of the ripple-related event listeners to be passive for improved performance and to prevent Chrome from logging warnings for almost every Material component.
  • Loading branch information
crisbeto authored and andrewseguin committed Dec 13, 2017
1 parent 7c84490 commit 12feff7
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/cdk/platform/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let supportsPassiveEvents: boolean;
* See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
*/
export function supportsPassiveEventListeners(): boolean {
if (supportsPassiveEvents == null) {
if (supportsPassiveEvents == null && typeof window !== 'undefined') {
try {
window.addEventListener('test', null!, Object.defineProperty({}, 'passive', {
get: () => supportsPassiveEvents = true
Expand Down
27 changes: 15 additions & 12 deletions src/lib/core/ripple/ripple-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {ElementRef, NgZone} from '@angular/core';
import {Platform} from '@angular/cdk/platform';
import {Platform, supportsPassiveEventListeners} from '@angular/cdk/platform';
import {RippleRef, RippleState} from './ripple-ref';


Expand Down Expand Up @@ -58,6 +57,9 @@ export class RippleRenderer {
/** Time in milliseconds when the last touchstart event happened. */
private _lastTouchStartEvent: number;

/** Options that apply to all the event listeners that are bound by the renderer. */
private _eventOptions = supportsPassiveEventListeners() ? ({passive: true} as any) : false;

/** Ripple config for all ripples created by events. */
rippleConfig: RippleConfig = {};

Expand All @@ -70,12 +72,12 @@ export class RippleRenderer {
this._containerElement = elementRef.nativeElement;

// Specify events which need to be registered on the trigger.
this._triggerEvents.set('mousedown', this.onMousedown.bind(this));
this._triggerEvents.set('mouseup', this.onPointerUp.bind(this));
this._triggerEvents.set('mouseleave', this.onPointerUp.bind(this));
this._triggerEvents.set('mousedown', this.onMousedown);
this._triggerEvents.set('mouseup', this.onPointerUp);
this._triggerEvents.set('mouseleave', this.onPointerUp);

this._triggerEvents.set('touchstart', this.onTouchStart.bind(this));
this._triggerEvents.set('touchend', this.onPointerUp.bind(this));
this._triggerEvents.set('touchstart', this.onTouchStart);
this._triggerEvents.set('touchend', this.onPointerUp);

// By default use the host element as trigger element.
this.setTriggerElement(this._containerElement);
Expand Down Expand Up @@ -173,22 +175,23 @@ export class RippleRenderer {
// Remove all previously register event listeners from the trigger element.
if (this._triggerElement) {
this._triggerEvents.forEach((fn, type) => {
this._triggerElement!.removeEventListener(type, fn);
this._triggerElement!.removeEventListener(type, fn, this._eventOptions);
});
}

if (element) {
// If the element is not null, register all event listeners on the trigger element.
this._ngZone.runOutsideAngular(() => {
this._triggerEvents.forEach((fn, type) => element.addEventListener(type, fn));
this._triggerEvents.forEach((fn, type) =>
element.addEventListener(type, fn, this._eventOptions));
});
}

this._triggerElement = element;
}

/** Function being called whenever the trigger is being pressed using mouse. */
private onMousedown(event: MouseEvent) {
private onMousedown = (event: MouseEvent) => {
const isSyntheticEvent = this._lastTouchStartEvent &&
Date.now() < this._lastTouchStartEvent + IGNORE_MOUSE_EVENTS_TIMEOUT;

Expand All @@ -199,7 +202,7 @@ export class RippleRenderer {
}

/** Function being called whenever the trigger is being pressed using touch. */
private onTouchStart(event: TouchEvent) {
private onTouchStart = (event: TouchEvent) => {
if (!this.rippleDisabled) {
// Some browsers fire mouse events after a `touchstart` event. Those synthetic mouse
// events will launch a second ripple if we don't ignore mouse events for a specific
Expand All @@ -212,7 +215,7 @@ export class RippleRenderer {
}

/** Function being called whenever the trigger is being released. */
private onPointerUp() {
private onPointerUp = () => {
if (!this._isPointerDown) {
return;
}
Expand Down

0 comments on commit 12feff7

Please sign in to comment.