Skip to content

Commit

Permalink
Add support for passive event listeners
Browse files Browse the repository at this point in the history
Provide a way for elements to have passive gesture listeners with
this._passiveGestures = true;

Fixes #4667
Fixes #3787
Fixes #3604
  • Loading branch information
dfreedm committed Jul 5, 2017
1 parent d1ad0c3 commit 40e8dc7
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 37 deletions.
12 changes: 6 additions & 6 deletions lib/legacy/legacy-element-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,9 @@
* @param {Element} node Element to add event listener to.
* @param {string} eventName Name of event to listen for.
* @param {string} methodName Name of handler method on `this` to call.
* @param {Object=} options Event listener options
*/
listen(node, eventName, methodName) {
node = node || this;
listen(node = this, eventName, methodName, options) {
let hbl = this.__boundListeners ||
(this.__boundListeners = new WeakMap());
let bl = hbl.get(node);
Expand All @@ -360,7 +360,7 @@
let key = eventName + methodName;
if (!bl[key]) {
bl[key] = this._addMethodEventListenerToNode(
node, eventName, methodName, this);
node, eventName, methodName, this, options);
}
}

Expand All @@ -371,15 +371,15 @@
* @param {Element} node Element to remove event listener from.
* @param {string} eventName Name of event to stop listening to.
* @param {string} methodName Name of handler method on `this` to not call
* @param {Object=} options Event Listener options
anymore.
*/
unlisten(node, eventName, methodName) {
node = node || this;
unlisten(node = this, eventName, methodName, options) {
let bl = this.__boundListeners && this.__boundListeners.get(node);
let key = eventName + methodName;
let handler = bl && bl[key];
if (handler) {
this._removeEventListenerFromNode(node, eventName, handler);
this._removeEventListenerFromNode(node, eventName, handler, options);
bl[key] = null;
}
}
Expand Down
22 changes: 16 additions & 6 deletions lib/mixins/gesture-event-listeners.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,25 @@
*/
class GestureEventListeners extends superClass {

_addEventListenerToNode(node, eventName, handler) {
if (!gestures.addListener(node, eventName, handler)) {
super._addEventListenerToNode(node, eventName, handler);
/** @override */
_addEventListenerToNode(node, eventName, handler, options) {
let gestureOptions = options;
if (this._passiveGestures && gestures.SUPPORTS_PASSIVE && !gestureOptions) {
gestureOptions = {'passive': true};
}
if (!gestures.addListener(node, eventName, handler, gestureOptions)) {
super._addEventListenerToNode(node, eventName, handler, options);
}
}

_removeEventListenerFromNode(node, eventName, handler) {
if (!gestures.removeListener(node, eventName, handler)) {
super._removeEventListenerFromNode(node, eventName, handler);
/** @override */
_removeEventListenerFromNode(node, eventName, handler, options) {
let gestureOptions = options;
if (this._passiveGestures && gestures.SUPPORTS_PASSIVE && !gestureOptions) {
gestureOptions = {'passive': true};
}
if (!gestures.removeListener(node, eventName, handler, gestureOptions)) {
super._removeEventListenerFromNode(node, eventName, handler, options);
}
}

Expand Down
15 changes: 9 additions & 6 deletions lib/mixins/template-stamp.html
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,13 @@
* @param {string} methodName Name of method
* @param {*=} context Context the method will be called on (defaults
* to `node`)
* @param {Object=} options Event Listener options
* @return {Function} Generated handler function
*/
_addMethodEventListenerToNode(node, eventName, methodName, context) {
_addMethodEventListenerToNode(node, eventName, methodName, context, options) {
context = context || node;
let handler = createNodeEventHandler(context, eventName, methodName);
this._addEventListenerToNode(node, eventName, handler);
this._addEventListenerToNode(node, eventName, handler, options);
return handler;
}

Expand All @@ -455,9 +456,10 @@
* @param {Node} node Node to add event listener to
* @param {string} eventName Name of event
* @param {Function} handler Listener function to add
* @param {Object=} options Event Handler options
*/
_addEventListenerToNode(node, eventName, handler) {
node.addEventListener(eventName, handler);
_addEventListenerToNode(node, eventName, handler, options) {
node.addEventListener(eventName, handler, options);
}

/**
Expand All @@ -466,9 +468,10 @@
* @param {Node} node Node to remove event listener from
* @param {string} eventName Name of event
* @param {Function} handler Listener function to remove
* @param {Object=} options Event Handler options
*/
_removeEventListenerFromNode(node, eventName, handler) {
node.removeEventListener(eventName, handler);
_removeEventListenerFromNode(node, eventName, handler, options) {
node.removeEventListener(eventName, handler, options);
}

}
Expand Down
39 changes: 23 additions & 16 deletions lib/utils/gestures.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@
let SUPPORTS_PASSIVE = false;
(function() {
try {
let opts = Object.defineProperty({}, 'passive', {get: function() {SUPPORTS_PASSIVE = true;}})
let opts = Object.defineProperty({}, 'passive', {get() {SUPPORTS_PASSIVE = true;}})
window.addEventListener('test', null, opts);
window.removeEventListener('test', null, opts);
} catch(e) {}
})();

let PASSIVE = SUPPORTS_PASSIVE ? {'passive': true} : false;

// Check for touch-only devices
let IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);

Expand Down Expand Up @@ -195,20 +197,20 @@
function trackDocument(stateObj, movefn, upfn) {
stateObj.movefn = movefn;
stateObj.upfn = upfn;
document.addEventListener('mousemove', movefn);
document.addEventListener('mouseup', upfn);
document.addEventListener('mousemove', movefn, PASSIVE);
document.addEventListener('mouseup', upfn, PASSIVE);
}

function untrackDocument(stateObj) {
document.removeEventListener('mousemove', stateObj.movefn);
document.removeEventListener('mouseup', stateObj.upfn);
document.removeEventListener('mousemove', stateObj.movefn, PASSIVE);
document.removeEventListener('mouseup', stateObj.upfn, PASSIVE);
stateObj.movefn = null;
stateObj.upfn = null;
}

// use a document-wide touchend listener to start the ghost-click prevention mechanism
// Use passive event listeners, if supported, to not affect scrolling performance
document.addEventListener('touchend', ignoreMouse, SUPPORTS_PASSIVE ? {passive: true} : false);
document.addEventListener('touchend', ignoreMouse, PASSIVE);

/**
* Module for adding listeners to a node for the following normalized
Expand All @@ -225,6 +227,7 @@
const Gestures = {
gestures: {},
recognizers: [],
SUPPORTS_PASSIVE,

/**
* Finds the element rendered on the screen at the provided coordinates.
Expand Down Expand Up @@ -379,11 +382,12 @@
* @param {Node} node Node to add listener on
* @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
* @param {Function} handler Event listener function to call
* @param {Object=} options Event Listener options
* @return {boolean} Returns true if a gesture event listener was added.
*/
addListener: function(node, evType, handler) {
addListener: function(node, evType, handler, options) {
if (this.gestures[evType]) {
this._add(node, evType, handler);
this._add(node, evType, handler, options);
return true;
}
},
Expand All @@ -396,11 +400,12 @@
* @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
* @param {Function} handler Event listener function previously passed to
* `addListener`.
* @param {Object=} options Event Listener options
* @return {boolean} Returns true if a gesture event listener was removed.
*/
removeListener: function(node, evType, handler) {
removeListener: function(node, evType, handler, options) {
if (this.gestures[evType]) {
this._remove(node, evType, handler);
this._remove(node, evType, handler, options);
return true;
}
},
Expand All @@ -412,8 +417,9 @@
* @param {HTMLElement} node Node on which to add the event.
* @param {string} evType Event type to add.
* @param {function} handler Event handler function.
* @param {Object=} options Event Listener options
*/
_add: function(node, evType, handler) {
_add: function(node, evType, handler, options) {
let recognizer = this.gestures[evType];
let deps = recognizer.deps;
let name = recognizer.name;
Expand All @@ -432,12 +438,12 @@
gobj[dep] = gd = {_count: 0};
}
if (gd._count === 0) {
node.addEventListener(dep, this._handleNative);
node.addEventListener(dep, this._handleNative, options);
}
gd[name] = (gd[name] || 0) + 1;
gd._count = (gd._count || 0) + 1;
}
node.addEventListener(evType, handler);
node.addEventListener(evType, handler, options);
if (recognizer.touchAction) {
this.setTouchAction(node, recognizer.touchAction);
}
Expand All @@ -450,8 +456,9 @@
* @param {HTMLElement} node Node on which to remove the event.
* @param {string} evType Event type to remove.
* @param {function} handler Event handler function.
* @param {Object=} options Event Listener options
*/
_remove: function(node, evType, handler) {
_remove: function(node, evType, handler, options) {
let recognizer = this.gestures[evType];
let deps = recognizer.deps;
let name = recognizer.name;
Expand All @@ -464,12 +471,12 @@
gd[name] = (gd[name] || 1) - 1;
gd._count = (gd._count || 1) - 1;
if (gd._count === 0) {
node.removeEventListener(dep, this._handleNative);
node.removeEventListener(dep, this._handleNative, options);
}
}
}
}
node.removeEventListener(evType, handler);
node.removeEventListener(evType, handler, options);
},

/**
Expand Down
6 changes: 3 additions & 3 deletions lib/utils/templatize.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,20 @@
/**
* @override
*/
_addEventListenerToNode(node, eventName, handler) {
_addEventListenerToNode(node, eventName, handler, options) {
if (this._methodHost && this.__templatizeOptions.parentModel) {
// If this instance should be considered a parent model, decorate
// events this template instance as `model`
this._methodHost._addEventListenerToNode(node, eventName, (e) => {
e.model = this;
handler(e);
});
}, options);
} else {
// Otherwise delegate to the template's host (which could be)
// another template instance
let templateHost = this.__dataHost.__dataHost;
if (templateHost) {
templateHost._addEventListenerToNode(node, eventName, handler);
templateHost._addEventListenerToNode(node, eventName, handler, options);
}
}
}
Expand Down

0 comments on commit 40e8dc7

Please sign in to comment.