From 3dd9a13ca5b2e25c333b96e7999ad0125aba4c6a Mon Sep 17 00:00:00 2001 From: Dominic Carretto Date: Thu, 18 May 2017 12:23:08 -0400 Subject: [PATCH] perf(ripple): Use passive event listeners on adapter instantiation (#649) Resolves #629 --- packages/mdc-ripple/index.js | 8 +++++--- packages/mdc-ripple/util.js | 18 ++++++++++++++++++ test/unit/mdc-ripple/util.test.js | 22 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/packages/mdc-ripple/index.js b/packages/mdc-ripple/index.js index f4919b2fb96..1e6e7acc6ac 100644 --- a/packages/mdc-ripple/index.js +++ b/packages/mdc-ripple/index.js @@ -16,7 +16,7 @@ import {MDCComponent} from '@material/base'; import MDCRippleFoundation from './foundation'; -import {supportsCssVariables, getMatchesProperty} from './util'; +import {applyPassive, supportsCssVariables, getMatchesProperty} from './util'; export {MDCRippleFoundation}; @@ -40,8 +40,10 @@ export class MDCRipple extends MDCComponent { isSurfaceDisabled: () => instance.disabled, addClass: (className) => instance.root_.classList.add(className), removeClass: (className) => instance.root_.classList.remove(className), - registerInteractionHandler: (evtType, handler) => instance.root_.addEventListener(evtType, handler), - deregisterInteractionHandler: (evtType, handler) => instance.root_.removeEventListener(evtType, handler), + registerInteractionHandler: (evtType, handler) => + instance.root_.addEventListener(evtType, handler, applyPassive()), + deregisterInteractionHandler: (evtType, handler) => + instance.root_.removeEventListener(evtType, handler, applyPassive()), registerResizeHandler: (handler) => window.addEventListener('resize', handler), deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler), updateCssVariable: (varName, value) => instance.root_.style.setProperty(varName, value), diff --git a/packages/mdc-ripple/util.js b/packages/mdc-ripple/util.js index 42b6b817cc8..721312efb41 100644 --- a/packages/mdc-ripple/util.js +++ b/packages/mdc-ripple/util.js @@ -14,6 +14,8 @@ * limitations under the License. */ +let supportsPassive_; + export function supportsCssVariables(windowObj) { const supportsFunctionPresent = windowObj.CSS && typeof windowObj.CSS.supports === 'function'; if (!supportsFunctionPresent) { @@ -30,6 +32,22 @@ export function supportsCssVariables(windowObj) { return explicitlySupportsCssVars || weAreFeatureDetectingSafari10plus; } +// Determine whether the current browser supports passive event listeners, and if so, use them. +export function applyPassive(globalObj = window, forceRefresh = false) { + if (supportsPassive_ === undefined || forceRefresh) { + let isSupported = false; + try { + globalObj.document.addEventListener('test', null, {get passive() { + isSupported = true; + }}); + } catch (e) { } + + supportsPassive_ = isSupported; + } + + return supportsPassive_ ? {passive: true} : false; +} + export function getMatchesProperty(HTMLElementPrototype) { return [ 'webkitMatchesSelector', 'msMatchesSelector', 'matches', diff --git a/test/unit/mdc-ripple/util.test.js b/test/unit/mdc-ripple/util.test.js index e750f8fd302..d25a5de01fe 100644 --- a/test/unit/mdc-ripple/util.test.js +++ b/test/unit/mdc-ripple/util.test.js @@ -75,6 +75,28 @@ test('#supportsCssVariables returns false when CSS is not an object', () => { assert.isNotOk(util.supportsCssVariables(windowObj)); }); +test('applyPassive returns an options object for browsers that support passive event listeners', () => { + const mockWindow = { + document: { + addEventListener: function(name, method, options) { + return options.passive; + }, + }, + }; + assert.deepEqual(util.applyPassive(mockWindow, true), {passive: true}); +}); + +test('applyPassive returns false for browsers that do not support passive event listeners', () => { + const mockWindow = { + document: { + addEventListener: function() { + throw new Error(); + }, + }, + }; + assert.isNotOk(util.applyPassive(mockWindow, true)); +}); + test('#getMatchesProperty returns the correct property for selector matching', () => { assert.equal(util.getMatchesProperty({matches: () => {}}), 'matches'); assert.equal(util.getMatchesProperty({webkitMatchesSelector: () => {}}), 'webkitMatchesSelector');