Skip to content

Commit

Permalink
Force touchstart, touchmove, and wheel listeners to be non-passive
Browse files Browse the repository at this point in the history
Yes, Chrome, yes WebKit: we really do intend to prevent the default scroll behavior for these events.

This is the only way to silence the console warnings from Chrome and support iOS Safari 11.3+.
  • Loading branch information
jfirebaugh committed Mar 2, 2018
1 parent 6adf891 commit 02d6528
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 26 deletions.
27 changes: 14 additions & 13 deletions src/ui/bind_handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const {
MapTouchEvent,
MapWheelEvent
} = require('../ui/events');
const DOM = require('../util/dom');

import type Map from './map';

Expand All @@ -30,19 +31,19 @@ module.exports = function bindHandlers(map: Map, options: {}) {
}
}

el.addEventListener('mouseout', onMouseOut, false);
el.addEventListener('mousedown', onMouseDown, false);
el.addEventListener('mouseup', onMouseUp, false);
el.addEventListener('mousemove', onMouseMove, false);
el.addEventListener('mouseover', onMouseOver, false);
el.addEventListener('touchstart', onTouchStart, false);
el.addEventListener('touchend', onTouchEnd, false);
el.addEventListener('touchmove', onTouchMove, false);
el.addEventListener('touchcancel', onTouchCancel, false);
el.addEventListener('click', onClick, false);
el.addEventListener('dblclick', onDblClick, false);
el.addEventListener('contextmenu', onContextMenu, false);
el.addEventListener('wheel', onWheel, false);
DOM.addEventListener(el, 'mouseout', onMouseOut);
DOM.addEventListener(el, 'mousedown', onMouseDown);
DOM.addEventListener(el, 'mouseup', onMouseUp);
DOM.addEventListener(el, 'mousemove', onMouseMove);
DOM.addEventListener(el, 'mouseover', onMouseOver);
DOM.addEventListener(el, 'touchstart', onTouchStart, {passive: false});
DOM.addEventListener(el, 'touchmove', onTouchMove, {passive: true}); // `passive: true` because onTouchMove only sends a map event.
DOM.addEventListener(el, 'touchend', onTouchEnd); // The real action is in DragPanHandler and TouchZoomRotateHandler.
DOM.addEventListener(el, 'touchcancel', onTouchCancel);
DOM.addEventListener(el, 'click', onClick);
DOM.addEventListener(el, 'dblclick', onDblClick);
DOM.addEventListener(el, 'contextmenu', onContextMenu);
DOM.addEventListener(el, 'wheel', onWheel, {passive: false});

function onMouseDown(e: MouseEvent) {
mouseDown = true;
Expand Down
18 changes: 9 additions & 9 deletions src/ui/handler/drag_pan.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ class DragPanHandler {
// window-level event listeners give us the best shot at capturing events that
// fall outside the map canvas element. Use `{capture: true}` for the move event
// to prevent map move events from being fired during a drag.
window.document.addEventListener('mousemove', this._onMove, {capture: true});
window.document.addEventListener('mouseup', this._onMouseUp);
DOM.addEventListener(window.document, 'mousemove', this._onMove, {capture: true});
DOM.addEventListener(window.document, 'mouseup', this._onMouseUp);

this._start(e);
}
Expand All @@ -127,8 +127,8 @@ class DragPanHandler {
// window-level event listeners give us the best shot at capturing events that
// fall outside the map canvas element. Use `{capture: true}` for the move event
// to prevent map move events from being fired during a drag.
window.document.addEventListener('touchmove', this._onMove, {capture: true});
window.document.addEventListener('touchend', this._onTouchEnd);
DOM.addEventListener(window.document, 'touchmove', this._onMove, {capture: true, passive: false});
DOM.addEventListener(window.document, 'touchend', this._onTouchEnd);

this._start(e);
}
Expand Down Expand Up @@ -236,11 +236,11 @@ class DragPanHandler {
}

_unbind() {
window.document.removeEventListener('touchmove', this._onMove, {capture: true});
window.document.removeEventListener('touchend', this._onTouchEnd);
window.document.removeEventListener('mousemove', this._onMove, {capture: true});
window.document.removeEventListener('mouseup', this._onMouseUp);
window.removeEventListener('blur', this._onBlur);
DOM.removeEventListener(window.document, 'touchmove', this._onMove, {capture: true, passive: false});
DOM.removeEventListener(window.document, 'touchend', this._onTouchEnd);
DOM.removeEventListener(window.document, 'mousemove', this._onMove, {capture: true});
DOM.removeEventListener(window.document, 'mouseup', this._onMouseUp);
DOM.removeEventListener(window, 'blur', this._onBlur);
}

_deactivate() {
Expand Down
8 changes: 4 additions & 4 deletions src/ui/handler/touch_zoom_rotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ class TouchZoomRotateHandler {
this._gestureIntent = undefined;
this._inertia = [];

window.document.addEventListener('touchmove', this._onMove, false);
window.document.addEventListener('touchend', this._onEnd, false);
DOM.addEventListener(window.document, 'touchmove', this._onMove, {passive: false});
DOM.addEventListener(window.document, 'touchend', this._onEnd);
}

_getTouchEventData(e: TouchEvent) {
Expand Down Expand Up @@ -196,8 +196,8 @@ class TouchZoomRotateHandler {
}

_onEnd(e: TouchEvent) {
window.document.removeEventListener('touchmove', this._onMove);
window.document.removeEventListener('touchend', this._onEnd);
DOM.removeEventListener(window.document, 'touchmove', this._onMove, {passive: false});
DOM.removeEventListener(window.document, 'touchend', this._onEnd);

const gestureIntent = this._gestureIntent;
const startScale = this._startScale;
Expand Down
31 changes: 31 additions & 0 deletions src/util/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,37 @@ exports.setTransform = function(el: HTMLElement, value: string) {
(el.style: any)[transformProp] = value;
};

// Feature detection for {passive: false} support in add/removeEventListener.
let passiveSupported = false;

try {
const options = (Object.defineProperty: any)({}, "passive", {
get: function() {
passiveSupported = true;
}
});
(window.addEventListener: any)("test", options, options);
(window.removeEventListener: any)("test", options, options);
} catch (err) {
passiveSupported = false;
}

exports.addEventListener = function(target: *, type: *, callback: *, options: {passive?: boolean, capture?: boolean} = {}) {
if ('passive' in options && passiveSupported) {
target.addEventListener(type, callback, (options: any));
} else {
target.addEventListener(type, callback, options.capture);
}
};

exports.removeEventListener = function(target: *, type: *, callback: *, options: {passive?: boolean, capture?: boolean} = {}) {
if ('passive' in options && passiveSupported) {
target.removeEventListener(type, callback, (options: any));
} else {
target.removeEventListener(type, callback, options.capture);
}
};

// Suppress the next click, but only if it's immediate.
const suppressClick: MouseEventListener = function (e) {
e.preventDefault();
Expand Down

0 comments on commit 02d6528

Please sign in to comment.