Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
Workaround touch event bugs on iOS
Browse files Browse the repository at this point in the history
On iOS, if an element is created from a document that is not window.document, touch event listeners will not work
correctly, and may crash the browser if the element is removed.

Therefore, for iOS we implement only one touch listener on document, and use target finding on touchstart.

These broken touch handlers are very easy to make with the Polymer declarative gesture system, but this is a bug for
Safari to fix.

Fixes #56 Work around safari crasher
Fixes #33 Work around safari touchevent listener bug
  • Loading branch information
dfreedm committed Sep 9, 2014
1 parent 60f7e32 commit 39cf501
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 30 deletions.
40 changes: 26 additions & 14 deletions src/dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
* - pointercancel: a pointer will no longer generate events
*/
var dispatcher = {
IS_IOS: false,
pointermap: new scope.PointerMap(),
requiredGestures: new scope.PointerMap(),
eventMap: Object.create(null),
Expand Down Expand Up @@ -189,6 +190,23 @@
this.fireEvent('up', inEvent);
this.requiredGestures.delete(inEvent.pointerId);
},
addGestureDependency: function(node, currentGestures) {
var gesturesWanted = node._pgEvents;
if (gesturesWanted) {
var gk = Object.keys(gesturesWanted);
for (var i = 0, r, ri, g; i < gk.length; i++) {
// gesture
g = gk[i];
if (gesturesWanted[g] > 0) {
// lookup gesture recognizer
r = this.dependencyMap[g];
// recognizer index
ri = r ? r.index : -1;
currentGestures[ri] = true;
}
}
}
},
// LISTENER LOGIC
eventHandler: function(inEvent) {
// This is used to prevent multiple dispatch of events from
Expand All @@ -202,21 +220,15 @@
if (!inEvent._handledByPG) {
currentGestures = {};
}
// map gesture names to ordered set of recognizers
var gesturesWanted = inEvent.currentTarget._pgEvents;
if (gesturesWanted) {
var gk = Object.keys(gesturesWanted);
for (var i = 0, r, ri, g; i < gk.length; i++) {
// gesture
g = gk[i];
if (gesturesWanted[g] > 0) {
// lookup gesture recognizer
r = this.dependencyMap[g];
// recognizer index
ri = r ? r.index : -1;
currentGestures[ri] = true;
}
// in IOS mode, there is only a listener on the document, so this is not re-entrant
if (this.IS_IOS) {
var nodes = scope.targetFinding.path(inEvent);
for (var i = 0, n; i < nodes.length; i++) {
n = nodes[i];
this.addGestureDependency(n, currentGestures);
}
} else {
this.addGestureDependency(inEvent.currentTarget, currentGestures);
}
}

Expand Down
20 changes: 8 additions & 12 deletions src/platform-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Included are touch events (v1), mouse events, and MSPointerEvents.
*/
(function(scope) {

var dispatcher = scope.dispatcher;
var nav = window.navigator;

Expand All @@ -24,19 +25,14 @@
dispatcher.registerSource('mouse', scope.mouseEvents);
if (window.ontouchstart !== undefined) {
dispatcher.registerSource('touch', scope.touchEvents);
/*
* NOTE: an empty touch listener on body will reactivate nodes imported from templates with touch listeners
* Removing it will re-break the nodes
*
* Work around for https://bugs.webkit.org/show_bug.cgi?id=135628
*/
var isSafari = nav.userAgent.match('Safari') && !nav.userAgent.match('Chrome');
if (isSafari) {
document.addEventListener('DOMContentLoaded', function() {
document.body.addEventListener('touchstart', function(){});
});
}
}
}

// Work around iOS bugs https://bugs.webkit.org/show_bug.cgi?id=135628 and https://bugs.webkit.org/show_bug.cgi?id=136506
var IS_IOS = navigator.userAgent.match('Safari') && !navigator.userAgent.match('Chrome') && 'ontouchstart' in window;

dispatcher.IS_IOS = IS_IOS;
scope.touchEvents.IS_IOS = IS_IOS;

dispatcher.register(document, true);
})(window.PolymerGestures);
14 changes: 14 additions & 0 deletions src/targetfind.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@
insideNode: function(node, x, y) {
var rect = node.getBoundingClientRect();
return (rect.left <= x) && (x <= rect.right) && (rect.top <= y) && (y <= rect.bottom);
},
path: function(event) {
var p;
if (HAS_FULL_PATH && event.path.length) {
p = event.path;
} else {
p = [];
var n = this.findTarget(event);
while (n) {
p.push(n);
n = n.parentNode || n.host;
}
}
return p;
}
};
scope.targetFinding = target;
Expand Down
10 changes: 6 additions & 4 deletions src/touch.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

// handler block for native touch events
var touchEvents = {
IS_IOS: false,
events: [
'touchstart',
'touchmove',
Expand All @@ -35,13 +36,14 @@
'move'
],
register: function(target, initial) {
if (initial) {
return;
if (this.IS_IOS ? initial : !initial) {
dispatcher.listen(target, this.events);
}
dispatcher.listen(target, this.events);
},
unregister: function(target) {
dispatcher.unlisten(target, this.events);
if (!this.IS_IOS) {
dispatcher.unlisten(target, this.events);
}
},
scrollTypes: {
EMITTER: 'none',
Expand Down

0 comments on commit 39cf501

Please sign in to comment.