Skip to content

Commit 9cbc2f6

Browse files
committed
event-tap now has preventDefault(), works on Android 4.0.x and on mouse/touch enabled devices.
event-tap got a bunch of modifications in this commit: - Dual listener support! Listens to "touchstart mousedown", and based on which one is captured, sets up listeners for either "touchmove", "touchend", "touchcancel", OR the equivalent mouse events. Now, touch events obviously fire a corresponding mouse event, and I didn't want to have a heavy-handed event.preventDefault() in the code. To prevent the corresponding mouse/click events from firing `touchEnd()`, I set up a `needToCancel` boolean that is toggled once the first `touchstart/mousedown` is captured. - e.preventDefault() works! You can now do this: ``` node.on('tap', function (e) { e.preventDefault(); //do some stuff here... }); ```
1 parent 3cc15ef commit 9cbc2f6

File tree

1 file changed

+66
-32
lines changed

1 file changed

+66
-32
lines changed

src/event/js/tap.js

+66-32
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,19 @@ var doc = Y.config.doc,
2525
GESTURE_MAP = Y.Event._GESTURE_MAP,
2626
SUPPORTS_TOUCHES = !!(doc && doc.createTouch),
2727
EVT_START = GESTURE_MAP.start,
28-
EVT_MOVE = GESTURE_MAP.move,
29-
EVT_END = GESTURE_MAP.end,
30-
EVT_CANCEL = GESTURE_MAP.cancel,
3128
EVT_TAP = 'tap',
3229

3330
HANDLES = {
3431
START: 'Y_TAP_ON_START_HANDLE',
35-
MOVE: 'Y_TAP_ON_MOVE_HANDLE',
3632
END: 'Y_TAP_ON_END_HANDLE',
3733
CANCEL: 'Y_TAP_ON_CANCEL_HANDLE'
3834
};
3935

40-
function detachHelper(subscription, handles, subset, context) {
36+
function detachHelper(subscription, handles, subset) {
4137

42-
handles = subset ? handles : [ handles.START, handles.MOVE, handles.END, handles.CANCEL ];
38+
handles = subset ? handles : [ handles.START, handles.END, handles.CANCEL ];
4339

44-
Y.Array.each(handles, function (item, index, array) {
40+
Y.Array.each(handles, function (item) {
4541
var handle = subscription[item];
4642
if (handle) {
4743
handle.detach();
@@ -82,6 +78,42 @@ Y.Event.define(EVT_TAP, {
8278
@static
8379
**/
8480
on: function (node, subscription, notifier) {
81+
82+
/*
83+
Patch synthetic event's fire method to allow for e.preventDefault()
84+
or e.stopPropagation() on a subsequent click event
85+
86+
Check https://gist.github.com/4272500 for some more info
87+
Check http://www.youtube.com/watch?v=v7Z6FlO1opU for the discussion.
88+
*/
89+
notifier.handle.evt.fire = function (e) {
90+
var subs = this._subscribers.concat(this._afters),
91+
args = Y.Array(arguments, 0, true),
92+
i, len, halt;
93+
94+
for (i = 0, len = subs.length; i < len; ++i) {
95+
if (subs[i]) {
96+
97+
//notify all the subscribers
98+
halt = subs[i].notify(args, this);
99+
100+
// stopImmediatePropagation
101+
if (halt === false || e.stopped > 1) {
102+
break;
103+
}
104+
}
105+
}
106+
107+
if (e.prevented || e.stopped) {
108+
e.target.once('click', function (clickEvt) {
109+
e.prevented && clickEvt.preventDefault();
110+
e.stopped && clickEvt[e.stopped === 2 ? 'stopImmediatePropagation' : 'stopPropagation']();
111+
});
112+
}
113+
114+
return !!this.stopped;
115+
};
116+
85117
subscription[HANDLES.START] = node.on(EVT_START, this.touchStart, this, node, subscription, notifier);
86118
},
87119

@@ -139,7 +171,6 @@ Y.Event.define(EVT_TAP, {
139171
detachHelper(subscription, HANDLES);
140172
},
141173

142-
143174
/**
144175
Called when the monitor(s) are tapped on, either through touchstart or mousedown.
145176
@@ -155,8 +186,10 @@ Y.Event.define(EVT_TAP, {
155186
touchStart: function (event, node, subscription, notifier, delegate) {
156187

157188
var context = {
158-
canceled: false
159-
};
189+
canceled: false,
190+
eventType: event.type
191+
},
192+
needToCancel = subscription.needToCancel || false;
160193
//move ways to quit early to the top.
161194

162195
// no right clicks
@@ -180,32 +213,30 @@ Y.Event.define(EVT_TAP, {
180213
context.startXY = [ event.pageX, event.pageY ];
181214
}
182215

183-
//Possibly outdated issue: something is off with the move that it attaches it but never triggers the handler
184-
subscription[HANDLES.MOVE] = node.once(EVT_MOVE, this.touchMove, this, node, subscription, notifier, delegate, context);
185-
subscription[HANDLES.END] = node.once(EVT_END, this.touchEnd, this, node, subscription, notifier, delegate, context);
186-
subscription[HANDLES.CANCEL] = node.once(EVT_CANCEL, this.touchMove, this, node, subscription, notifier, delegate, context);
187-
},
216+
//if `onTouchStart()` was called by a touch event, set up touch event subscriptions. Otherwise, set up mouse/pointer event event subscriptions.
217+
if (context.eventType.indexOf('touch') !== -1 && !needToCancel) {
188218

189-
/**
190-
Called when the monitor(s) fires a touchmove or touchcancel event (or the mouse equivalent).
191-
This method detaches event handlers so that 'tap' is not fired.
219+
subscription[HANDLES.END] = node.once('touchend', this.touchEnd, this, node, subscription, notifier, delegate, context);
220+
subscription[HANDLES.CANCEL] = node.once('touchcancel', this.detach, this, node, subscription, notifier, delegate, context);
192221

193-
@method touchMove
194-
@param {DOMEventFacade} event
195-
@param {Y.Node} node
196-
@param {Array} subscription
197-
@param {Boolean} notifier
198-
@param {Boolean} delegate
199-
@param {Object} context
200-
@protected
201-
@static
202-
**/
203-
touchMove: function (event, node, subscription, notifier, delegate, context) {
204-
detachHelper(subscription, [ HANDLES.MOVE, HANDLES.END, HANDLES.CANCEL ], true, context);
205-
context.cancelled = true;
222+
subscription.needToCancel = true;
223+
}
224+
else if (context.eventType.indexOf('mouse') !== -1 && !needToCancel) {
225+
subscription[HANDLES.END] = node.once('mouseup', this.touchEnd, this, node, subscription, notifier, delegate, context);
226+
subscription[HANDLES.CANCEL] = node.once('mousecancel', this.detach, this, node, subscription, notifier, delegate, context);
227+
228+
subscription.needToCancel = true;
229+
}
230+
else if (context.eventType.indexOf('MSPointer') !== -1 && !needToCancel) {
231+
subscription[HANDLES.END] = node.once('MSPointerUp', this.touchEnd, this, node, subscription, notifier, delegate, context);
232+
subscription[HANDLES.CANCEL] = node.once('MSPointerCancel', this.detach, this, node, subscription, notifier, delegate, context);
233+
234+
subscription.needToCancel = true;
235+
}
206236

207237
},
208238

239+
209240
/**
210241
Called when the monitor(s) fires a touchend event (or the mouse equivalent).
211242
This method fires the 'tap' event if certain requirements are met.
@@ -225,6 +256,8 @@ Y.Event.define(EVT_TAP, {
225256
endXY,
226257
clientXY;
227258

259+
subscription.needToCancel = false;
260+
228261
//There is a double check in here to support event simulation tests, in which
229262
//event.touches can be undefined when simulating 'touchstart' on touch devices.
230263
if (SUPPORTS_TOUCHES && event.changedTouches) {
@@ -239,7 +272,7 @@ Y.Event.define(EVT_TAP, {
239272
detachHelper(subscription, [ HANDLES.MOVE, HANDLES.END, HANDLES.CANCEL ], true, context);
240273

241274
// make sure mouse didn't move
242-
if (Math.abs(endXY[0] - startXY[0]) === 0 && Math.abs(endXY[1] - startXY[1]) === 0) {
275+
if (Math.abs(endXY[0] - startXY[0]) < 15 && Math.abs(endXY[1] - startXY[1]) < 15) {
243276

244277
event.type = EVT_TAP;
245278
event.pageX = endXY[0];
@@ -250,5 +283,6 @@ Y.Event.define(EVT_TAP, {
250283

251284
notifier.fire(event);
252285
}
286+
253287
}
254288
});

0 commit comments

Comments
 (0)