Skip to content

Commit db81943

Browse files
author
Sérgio Gomes
committed
perf(drawer): Use passive event listeners to improve drawer perf.
Passive event listeners improve touch events performance, by letting the browser know a listener commits to not cancelling events. Also fixes pointer events implementation.
1 parent ed895b7 commit db81943

File tree

7 files changed

+68
-50
lines changed

7 files changed

+68
-50
lines changed

demos/drawer/temporary-drawer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
Header here
7575
</div>
7676
</header>
77-
<nav class="mdl-list-group">
77+
<nav class="mdl-temporary-drawer__content mdl-list-group">
7878
<div id="icon-with-text-demo" class="mdl-list">
7979
<a class="mdl-list-item mdl-temporary-drawer--selected" href="#">
8080
<i class="material-icons mdl-list-item__start-detail" aria-hidden="true">inbox</i>Inbox

packages/mdl-drawer/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ for any display size.
8383
Header here
8484
</div>
8585
</header>
86-
<nav id="icon-with-text-demo" class="mdl-list">
86+
<nav id="icon-with-text-demo" class="mdl-temporary-drawer__content mdl-list">
8787
<a class="mdl-list-item mdl-temporary-drawer--selected" href="#">
8888
<i class="material-icons mdl-list-item__start-detail" aria-hidden="true">inbox</i>Inbox
8989
</a>
@@ -114,7 +114,7 @@ very useful for visual alignment and consistency. Note that you can place conten
114114

115115
<div class="mdl-temporary-drawer__toolbar-spacer"></div>
116116

117-
<nav id="icon-with-text-demo" class="mdl-list">
117+
<nav id="icon-with-text-demo" class="mdl-temporary-drawer__content mdl-list">
118118
<a class="mdl-list-item mdl-temporary-drawer--selected" href="#">
119119
<i class="material-icons mdl-list-item__start-detail" aria-hidden="true">inbox</i>Inbox
120120
</a>
@@ -142,7 +142,7 @@ for placing the actual content, which will be bottom-aligned.
142142
</div>
143143
</header>
144144

145-
<nav id="icon-with-text-demo" class="mdl-list">
145+
<nav id="icon-with-text-demo" class="mdl-temporary-drawer__content mdl-list">
146146
<a class="mdl-list-item mdl-temporary-drawer--selected" href="#">
147147
<i class="material-icons mdl-list-item__start-detail" aria-hidden="true">inbox</i>Inbox
148148
</a>
@@ -161,6 +161,7 @@ CSS classes:
161161
| -------------------------------------- | -------------------------------------------------------------------------- |
162162
| `mdl-temporary-drawer` | Mandatory. Needs to be set on the root element of the component. |
163163
| `mdl-temporary-drawer__drawer` | Mandatory. Needs to be set on the container node for the drawer content. |
164+
| `mdl-temporary-drawer__content` | Optional. Should be set on the list of items inside the drawer. |
164165
| `mdl-temporary-drawer__toolbar-spacer` | Optional. Add to node to provide the matching amount of space for toolbar. |
165166
| `mdl-temporary-drawer__header` | Optional. Add to container node to create a 16:9 drawer header. |
166167
| `mdl-temporary-drawer__header-content` | Optional. Add to content node inside `mdl-temporary-drawer__header`. |

packages/mdl-drawer/temporary/foundation.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,6 @@ export default class MDLTemporaryDrawerFoundation extends MDLFoundation {
204204
}
205205

206206
this.currentX_ = evt.touches ? evt.touches[0].pageX : evt.pageX;
207-
208-
if (this.direction_ * (this.currentX_ - this.startX_) < 0) {
209-
evt.preventDefault();
210-
}
211207
}
212208

213209
handleTouchEnd_(evt) {

packages/mdl-drawer/temporary/index.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ export class MDLTemporaryDrawer extends MDLComponent {
5050
removeClass: className => this.root_.classList.remove(className),
5151
hasClass: className => this.root_.classList.contains(className),
5252
hasNecessaryDom: () => Boolean(this.drawer),
53-
registerInteractionHandler: (evt, handler) => this.root_.addEventListener(util.remapEvent(evt), handler),
54-
deregisterInteractionHandler: (evt, handler) => this.root_.removeEventListener(util.remapEvent(evt), handler),
55-
registerDrawerInteractionHandler: (evt, handler) => this.drawer.addEventListener(util.remapEvent(evt), handler),
53+
registerInteractionHandler: (evt, handler) =>
54+
this.root_.addEventListener(util.remapEvent(evt), handler, util.applyPassive()),
55+
deregisterInteractionHandler: (evt, handler) =>
56+
this.root_.removeEventListener(util.remapEvent(evt), handler, util.applyPassive()),
57+
registerDrawerInteractionHandler: (evt, handler) =>
58+
this.drawer.addEventListener(util.remapEvent(evt), handler),
5659
deregisterDrawerInteractionHandler: (evt, handler) =>
5760
this.drawer.removeEventListener(util.remapEvent(evt), handler),
5861
registerTransitionEndHandler: handler => this.drawer.addEventListener('transitionend', handler),

packages/mdl-drawer/temporary/mdl-temporary-drawer.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
will-change: transform;
7171
box-sizing: border-box;
7272
overflow: hidden;
73+
touch-action: none;
7374

7475
@include mdl-rtl(".mdl-temporary-drawer") {
7576
transform: translateX(calc(100% + 20px));
@@ -89,6 +90,7 @@
8990
overflow-y: auto;
9091
box-sizing: border-box;
9192
-webkit-overflow-scrolling: touch;
93+
touch-action: pan-y;
9294
}
9395

9496
&__footer {

packages/mdl-drawer/util.js

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,10 @@ const TAB_DATA = 'data-mdl-tabindex';
1818
const TAB_DATA_HANDLED = 'data-mdl-tabindex-handled';
1919

2020
let storedTransformPropertyName_;
21+
let supportsPassive_;
2122

22-
/**
23-
* Remap touch events to pointer events, if the browser doesn't support touch events.
24-
* @param {string} eventName The requested event name.
25-
* @param {Object} globalObj The global object, useful for testing (defaults to window).
26-
* @return {string} The remapped event name.
27-
*/
28-
function remapEvent(eventName, globalObj = window) {
23+
// Remap touch events to pointer events, if the browser doesn't support touch events.
24+
export function remapEvent(eventName, globalObj = window) {
2925
if (!('ontouchstart' in globalObj.document)) {
3026
switch (eventName) {
3127
case 'touchstart':
@@ -42,53 +38,51 @@ function remapEvent(eventName, globalObj = window) {
4238
return eventName;
4339
}
4440

45-
/**
46-
* Choose the correct transform property to use on the current browser.
47-
* @param {Object} globalObj The global object, useful for testing (defaults to window).
48-
* @return {string} The transform property name.
49-
*/
50-
function getTransformPropertyName(globalObj = window) {
51-
if (storedTransformPropertyName_ === undefined || globalObj !== window) {
41+
// Choose the correct transform property to use on the current browser.
42+
export function getTransformPropertyName(globalObj = window, forceRefresh = false) {
43+
if (storedTransformPropertyName_ === undefined || forceRefresh) {
5244
const el = globalObj.document.createElement('div');
5345
const transformPropertyName = ('transform' in el.style ? 'transform' : '-webkit-transform');
54-
55-
if (globalObj === window) {
56-
storedTransformPropertyName_ = transformPropertyName;
57-
}
58-
return transformPropertyName;
46+
storedTransformPropertyName_ = transformPropertyName;
5947
}
6048

6149
return storedTransformPropertyName_;
6250
}
6351

64-
/**
65-
* Determine whether the current browser supports CSS properties.
66-
* @param {Object} globalObj The global object, useful for testing (defaults to window).
67-
* @return {boolean} Whether the current browser supports CSS properties.
68-
*/
69-
function supportsCssCustomProperties(globalObj = window) {
52+
// Determine whether the current browser supports CSS properties.
53+
export function supportsCssCustomProperties(globalObj = window) {
7054
if ('CSS' in globalObj) {
7155
return globalObj.CSS.supports('(--color: red)');
7256
}
7357
return false;
7458
}
7559

76-
/**
77-
* Save the tab state for an element.
78-
* @param {Element} el The element for which to save the tab state.
79-
*/
80-
function saveElementTabState(el) {
60+
// Determine whether the current browser supports passive event listeners, and if so, use them.
61+
export function applyPassive(globalObj = window, forceRefresh = false) {
62+
if (supportsPassive_ === undefined || forceRefresh) {
63+
let isSupported = false;
64+
try {
65+
globalObj.document.addEventListener('test', null, {get passive() {
66+
isSupported = true;
67+
}});
68+
} catch (e) { }
69+
70+
supportsPassive_ = isSupported;
71+
}
72+
73+
return supportsPassive_ ? {passive: true} : false;
74+
}
75+
76+
// Save the tab state for an element.
77+
export function saveElementTabState(el) {
8178
if (el.hasAttribute('tabindex')) {
8279
el.setAttribute(TAB_DATA, el.getAttribute('tabindex'));
8380
}
8481
el.setAttribute(TAB_DATA_HANDLED, true);
8582
}
8683

87-
/**
88-
* Restore the tab state for an element, if it was saved.
89-
* @param {Element} el The element for which to restore the tab state.
90-
*/
91-
function restoreElementTabState(el) {
84+
// Restore the tab state for an element, if it was saved.
85+
export function restoreElementTabState(el) {
9286
// Only modify elements we've already handled, in case anything was dynamically added since we saved state.
9387
if (el.hasAttribute(TAB_DATA_HANDLED)) {
9488
if (el.hasAttribute(TAB_DATA)) {
@@ -100,5 +94,3 @@ function restoreElementTabState(el) {
10094
el.removeAttribute(TAB_DATA_HANDLED);
10195
}
10296
}
103-
104-
export {remapEvent, getTransformPropertyName, supportsCssCustomProperties, saveElementTabState, restoreElementTabState};

test/unit/mdl-drawer/util.test.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ test('getTransformPropertyName returns "transform" for browsers that support it'
5555
}
5656
}
5757
};
58-
t.equal(utils.getTransformPropertyName(mockWindow), 'transform');
58+
t.equal(utils.getTransformPropertyName(mockWindow, true), 'transform');
5959
t.end();
6060
});
6161

@@ -67,7 +67,7 @@ test('getTransformPropertyName returns "-webkit-transform" for browsers that do
6767
}
6868
}
6969
};
70-
t.equal(utils.getTransformPropertyName(mockWindow), '-webkit-transform');
70+
t.equal(utils.getTransformPropertyName(mockWindow, true), '-webkit-transform');
7171
t.end();
7272
});
7373

@@ -91,6 +91,30 @@ test('supportsCssCustomProperties returns dalse for browsers that do not support
9191
t.end();
9292
});
9393

94+
test('applyPassive returns an options object for browsers that support passive event listeners', t => {
95+
const mockWindow = {
96+
document: {
97+
addEventListener: function(name, method, options) {
98+
return options.passive;
99+
}
100+
}
101+
};
102+
t.deepEqual(utils.applyPassive(mockWindow, true), {passive: true});
103+
t.end();
104+
});
105+
106+
test('applyPassive returns false for browsers that do not support passive event listeners', t => {
107+
const mockWindow = {
108+
document: {
109+
addEventListener: function() {
110+
throw new Error();
111+
}
112+
}
113+
};
114+
t.false(utils.applyPassive(mockWindow, true));
115+
t.end();
116+
});
117+
94118
test('saveElementTabState saves the tab index of an element', t => {
95119
const el = bel`<div id="foo" tabindex="42"></div>`;
96120
utils.saveElementTabState(el);

0 commit comments

Comments
 (0)