2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
8 YUI.add('event-tap', function (Y, NAME) {
11 The tap module provides a gesture events, "tap", which normalizes user interactions
12 across touch and mouse or pointer based input devices. This can be used by application developers
13 to build input device agnostic components which behave the same in response to either touch or mouse based
16 'tap' is like a touchscreen 'click', only it requires much less finger-down time since it listens to touch events,
17 but reverts to mouse events if touch is not supported.
21 YUI().use('event-tap', function (Y) {
22 Y.one('#my-button').on('tap', function (e) {
23 Y.log('Button was tapped on');
29 @author Andres Garza, matuzak and tilo mitra
33 var doc = Y.config.doc,
34 GESTURE_MAP = Y.Event._GESTURE_MAP,
35 EVT_START = GESTURE_MAP.start,
39 START: 'Y_TAP_ON_START_HANDLE',
40 END: 'Y_TAP_ON_END_HANDLE',
41 CANCEL: 'Y_TAP_ON_CANCEL_HANDLE'
44 function detachHandles(subscription, handles) {
45 handles = handles || Y.Object.values(HANDLES);
47 Y.Array.each(handles, function (item) {
48 var handle = subscription[item];
51 subscription[item] = null;
59 Sets up a "tap" event, that is fired on touch devices in response to a tap event (finger down, finder up).
60 This event can be used instead of listening for click events which have a 500ms delay on most touch devices.
61 This event can also be listened for using node.delegate().
64 @param type {string} "tap"
65 @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event.
67 @return {EventHandle} the detach handle
69 Y.Event.define(EVT_TAP, {
71 preventedFn: function (e) {
72 var sub = e.target.once('click', function (click) {
73 click.preventDefault();
76 // Make sure to detach the subscription during the next event loop
77 // so this doesn't `preventDefault()` on the wrong click event.
78 setTimeout(function () {
80 //Setting this to `0` causes the detachment to occur before the click
81 //comes in on Android 4.0.3-4.0.4. 100ms seems to be a reliable number here
82 //that works across the board.
87 processArgs: function (args, isDelegate) {
89 //if we return for the delegate use case, then the `filter` argument
90 //returns undefined, and we have to get the filter from sub._extra[0] (ugly)
94 // remove the extra arguments from the array as specified by
95 // http://yuilibrary.com/yui/docs/event/synths.html
101 This function should set up the node that will eventually fire the event.
105 node.on('tap', function (e) {
106 Y.log('the node was tapped on');
111 @param {Array} subscription
112 @param {Boolean} notifier
116 on: function (node, subscription, notifier) {
117 subscription[HANDLES.START] = node.on(EVT_START, this._start, this, node, subscription, notifier);
121 Detaches all event subscriptions set up by the event-tap module
125 @param {Array} subscription
126 @param {Boolean} notifier
130 detach: function (node, subscription, notifier) {
131 detachHandles(subscription);
135 Event delegation for the 'tap' event. The delegated event will use a
136 supplied selector or filtering function to test if the event references at least one
137 node that should trigger the subscription callback.
141 node.delegate('tap', function (e) {
142 Y.log('li a inside node was tapped.');
147 @param {Array} subscription
148 @param {Boolean} notifier
149 @param {String | Function} filter
153 delegate: function (node, subscription, notifier, filter) {
154 subscription[HANDLES.START] = node.delegate(EVT_START, function (e) {
155 this._start(e, node, subscription, notifier, true);
160 Detaches the delegated event subscriptions set up by the event-tap module.
161 Only used if you use node.delegate(...) instead of node.on(...);
163 @method detachDelegate
165 @param {Array} subscription
166 @param {Boolean} notifier
170 detachDelegate: function (node, subscription, notifier) {
171 detachHandles(subscription);
175 Called when the monitor(s) are tapped on, either through touchstart or mousedown.
178 @param {DOMEventFacade} event
180 @param {Array} subscription
181 @param {Boolean} notifier
182 @param {Boolean} delegate
186 _start: function (event, node, subscription, notifier, delegate) {
190 eventType: event.type
192 preventMouse = subscription.preventMouse || false;
194 //move ways to quit early to the top.
196 if (event.button && event.button === 3) {
200 // for now just support a 1 finger count (later enhance via config)
201 if (event.touches && event.touches.length !== 1) {
205 context.node = delegate ? event.currentTarget : node;
207 //There is a double check in here to support event simulation tests, in which
208 //event.touches can be undefined when simulating 'touchstart' on touch devices.
210 context.startXY = [ event.touches[0].pageX, event.touches[0].pageY ];
213 context.startXY = [ event.pageX, event.pageY ];
216 //If `onTouchStart()` was called by a touch event, set up touch event subscriptions.
217 //Otherwise, set up mouse/pointer event event subscriptions.
220 subscription[HANDLES.END] = node.once('touchend', this._end, this, node, subscription, notifier, delegate, context);
221 subscription[HANDLES.CANCEL] = node.once('touchcancel', this.detach, this, node, subscription, notifier, delegate, context);
223 //Since this is a touch* event, there will be corresponding mouse events
224 //that will be fired. We don't want these events to get picked up and fire
225 //another `tap` event, so we'll set this variable to `true`.
226 subscription.preventMouse = true;
229 //Only add these listeners if preventMouse is `false`
230 //ie: not when touch events have already been subscribed to
231 else if (context.eventType.indexOf('mouse') !== -1 && !preventMouse) {
232 subscription[HANDLES.END] = node.once('mouseup', this._end, this, node, subscription, notifier, delegate, context);
233 subscription[HANDLES.CANCEL] = node.once('mousecancel', this.detach, this, node, subscription, notifier, delegate, context);
236 //If a mouse event comes in after a touch event, it will go in here and
237 //reset preventMouse to `true`.
238 //If a mouse event comes in without a prior touch event, preventMouse will be
239 //false in any case, so this block doesn't do anything.
240 else if (context.eventType.indexOf('mouse') !== -1 && preventMouse) {
241 subscription.preventMouse = false;
244 else if (context.eventType.indexOf('MSPointer') !== -1) {
245 subscription[HANDLES.END] = node.once('MSPointerUp', this._end, this, node, subscription, notifier, delegate, context);
246 subscription[HANDLES.CANCEL] = node.once('MSPointerCancel', this.detach, this, node, subscription, notifier, delegate, context);
253 Called when the monitor(s) fires a touchend event (or the mouse equivalent).
254 This method fires the 'tap' event if certain requirements are met.
257 @param {DOMEventFacade} event
259 @param {Array} subscription
260 @param {Boolean} notifier
261 @param {Boolean} delegate
262 @param {Object} context
266 _end: function (event, node, subscription, notifier, delegate, context) {
267 var startXY = context.startXY,
272 if (subscription._extra && subscription._extra.sensitivity >= 0) {
273 sensitivity = subscription._extra.sensitivity;
276 //There is a double check in here to support event simulation tests, in which
277 //event.touches can be undefined when simulating 'touchstart' on touch devices.
278 if (event.changedTouches) {
279 endXY = [ event.changedTouches[0].pageX, event.changedTouches[0].pageY ];
280 clientXY = [event.changedTouches[0].clientX, event.changedTouches[0].clientY];
283 endXY = [ event.pageX, event.pageY ];
284 clientXY = [event.clientX, event.clientY];
287 // make sure mouse didn't move
288 if (Math.abs(endXY[0] - startXY[0]) <= sensitivity && Math.abs(endXY[1] - startXY[1]) <= sensitivity) {
290 event.type = EVT_TAP;
291 event.pageX = endXY[0];
292 event.pageY = endXY[1];
293 event.clientX = clientXY[0];
294 event.clientY = clientXY[1];
295 event.currentTarget = context.node;
297 notifier.fire(event);
300 detachHandles(subscription, [HANDLES.END, HANDLES.CANCEL]);
305 }, '3.13.0', {"requires": ["node-base", "event-base", "event-touch", "event-synthetic"]});