Skip to content

Commit

Permalink
Allow user prevention of tap and track gestures from down
Browse files Browse the repository at this point in the history
New API: `event.detail.prevent('tap')` and `event.detail.prevent('track')`
Fixes #1823

Forward `preventDefault` from gesture events to source events

`gesture.preventDefault()` is equivalent to
`gesture.detail.sourceEvent.preventDefault()`

Remove `preventDefault()` on mousemove in track, user must use
`track.preventDefault()` to hide user selection or use `user-select:none`
Fixes #1824
  • Loading branch information
dfreedm committed Jun 12, 2015
1 parent 1cad7b0 commit 11869f2
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 18 deletions.
69 changes: 51 additions & 18 deletions src/standard/gestures.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
}

var POINTERSTATE = {
tapPrevented: false,
mouse: {
target: null,
mouseIgnoreJob: null
Expand All @@ -99,7 +98,6 @@
return ta;
}


var Gestures = {
gestures: {},
recognizers: [],
Expand Down Expand Up @@ -252,6 +250,19 @@
}
},

findRecognizerByEvent: function(evName) {
for (var i = 0, r; i < this.recognizers.length; i++) {
r = this.recognizers[i];
for (var j = 0, n; j < r.emits.length; j++) {
n = r.emits[j];
if (n === evName) {
return r;
}
}
}
return null;
},

// set scrolling direction on node to check later on first move
// must call this before adding event listeners!
setTouchAction: function(node, value) {
Expand All @@ -268,6 +279,22 @@
cancelable: true
});
target.dispatchEvent(ev);

// forward `preventDefault` in a clean way
if (ev.defaultPrevented) {
var se = detail.sourceEvent;
// sourceEvent may be a touch, which is not preventable this way
if (se && se.preventDefault) {
se.preventDefault();
}
}
},

prevent: function(evName) {
var recognizer = this.findRecognizerByEvent(evName);
if (recognizer.info) {
recognizer.info.prevent = true;
}
}
};

Expand All @@ -293,10 +320,12 @@
this.fire('up', e.currentTarget, e.changedTouches[0]);
},
fire: function(type, target, event) {
var self = this;
Gestures.fire(target, type, {
x: event.clientX,
y: event.clientY,
sourceEvent: event
sourceEvent: event,
prevent: Gestures.prevent.bind(Gestures)
});
}
});
Expand All @@ -318,7 +347,8 @@
this.moves.shift();
}
this.moves.push(move);
}
},
prevent: false
},

clearInfo: function() {
Expand All @@ -327,9 +357,13 @@
this.info.moves = [];
this.info.x = 0;
this.info.y = 0;
this.info.prevent = false;
},

hasMovedEnough: function(x, y) {
if (this.info.prevent) {
return false;
}
if (this.info.started) {
return true;
}
Expand All @@ -348,13 +382,12 @@
self.info.state = self.info.started ? (e.type === 'mouseup' ? 'end' : 'track') : 'start';
self.info.addMove({x: x, y: y});
self.fire(t, e);
e.preventDefault();
self.info.started = true;
}
};
var upfn = function upfn(e) {
if (self.info.started) {
POINTERSTATE.tapPrevented = true;
Gestures.prevent('tap');
movefn(e);
}
self.clearInfo();
Expand Down Expand Up @@ -393,7 +426,7 @@
// only trackend if track was started and not aborted
if (this.info.started) {
// iff tracking, always prevent tap
POINTERSTATE.tapPrevented = true;
Gestures.prevent('tap');
// reset started state on up
this.info.state = 'end';
this.info.addMove({x: ct.clientX, y: ct.clientY});
Expand Down Expand Up @@ -433,21 +466,22 @@
name: 'tap',
deps: ['mousedown', 'click', 'touchstart', 'touchend'],
emits: ['tap'],
start: {
info: {
x: NaN,
y: NaN
y: NaN,
prevent: false
},
reset: function() {
this.start.x = NaN;
this.start.y = NaN;
this.info.x = NaN;
this.info.y = NaN;
this.info.prevent = false;
},
save: function(e) {
this.start.x = e.clientX;
this.start.y = e.clientY;
this.info.x = e.clientX;
this.info.y = e.clientY;
},

mousedown: function(e) {
POINTERSTATE.tapPrevented = false;
this.save(e);
},

Expand All @@ -456,20 +490,19 @@
},

touchstart: function(e) {
POINTERSTATE.tapPrevented = false;
this.save(e.changedTouches[0]);
},
touchend: function(e) {
this.forward(e.changedTouches[0]);
},

forward: function(e) {
var dx = Math.abs(e.clientX - this.start.x);
var dy = Math.abs(e.clientY - this.start.y);
var dx = Math.abs(e.clientX - this.info.x);
var dy = Math.abs(e.clientY - this.info.y);
// dx,dy can be NaN if `click` has been simulated and there was no `down` for `start`
if (isNaN(dx) || isNaN(dy) || (dx <= TAP_DISTANCE && dy <= TAP_DISTANCE)) {
// prevent taps from being generated if an event has canceled them
if (!POINTERSTATE.tapPrevented) {
if (!this.info.prevent) {
Gestures.fire(e.target, 'tap', {
x: e.clientX,
y: e.clientY,
Expand Down
11 changes: 11 additions & 0 deletions test/smoke/gestures.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
width: 20px;
background: blue;
}
#prevent {
height: 20px;
width: 20px;
background: red;
}
</style>
<div id="prevent" on-down="preventTap"></div>
<a href="#" on-click="linkclick" on-touchend="removeLink">LINK</a>
<div id="inner" on-tap="divtap"></div>
</template>
Expand All @@ -39,6 +45,11 @@
'click': 'logger',
'track': 'track'
},
preventTap: function(e, detail) {
detail.prevent('tap');
detail.prevent('track');
e.preventDefault();
},
logger: function(e, detail) {
console.log(e.type);
console.log(detail);
Expand Down
26 changes: 26 additions & 0 deletions test/unit/gestures-elements.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,29 @@
});
</script>
</dom-module>

<dom-module id="x-prevent">
<script>
Polymer({
listeners: {
'down': 'prevent',
'up': 'handle',
'tap': 'handle',
'track': 'handle'
},
is: 'x-prevent',
created: function() {
this.stream = [];
},
handle: function(e) {
this.stream.push(e);
},
prevent: function(e, detail) {
detail.prevent('tap');
detail.prevent('track');
e.preventDefault();
this.handle(e);
}
});
</script>
</dom-module>
43 changes: 43 additions & 0 deletions test/unit/gestures.html
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,49 @@
});
});

suite('Prevention', function() {
var el;
setup(function() {
el = document.createElement('x-prevent');
document.body.appendChild(el);
});
teardown(function() {
el.parentNode.removeChild(el);
});

test('tap', function() {
var ev = new CustomEvent('mousedown', {
cancelable: true
});
el.dispatchEvent(ev);
assert.equal(el.stream.length, 1);
assert.equal(el.stream[0].type, 'down');
assert.equal(el.stream[0].defaultPrevented, true);
assert.equal(ev.defaultPrevented, true);
});

test('track', function() {
var ev = new CustomEvent('mousedown', {
cancelable: true
});
ev.clientX = ev.clientY = 0;
el.dispatchEvent(ev);
assert.equal(el.stream.length, 1);
for (var i = 0; i < 10; i++) {
ev = new CustomEvent(
i === 9 ? 'mouseup' : 'mousemove',
{bubbles: true}
);
ev.clientX = ev.clientY = 10 * i;
el.dispatchEvent(ev);
}
assert.equal(el.stream.length, 2);
assert.equal(el.stream[0].type, 'down');
assert.equal(el.stream[0].defaultPrevented, true);
assert.equal(el.stream[1].type, 'up');
});
});

// TODO(dfreedm): Add more gesture tests!

</script>
Expand Down

0 comments on commit 11869f2

Please sign in to comment.