Skip to content

Instantly share code, notes, and snippets.

@RubaXa
Last active January 2, 2016 16:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RubaXa/8333650 to your computer and use it in GitHub Desktop.
Save RubaXa/8333650 to your computer and use it in GitHub Desktop.
/**
* jQuery event "tap" (Based on https://developers.google.com/mobile/articles/fast_buttons)
*
* @author RubaXa <trash@rubaxa.org>
* @license MIT
*/
(function (window, $){
var
support = window.TapSupportEnabled && ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch
, TOUCH_DELTA = 15 // const, touch -> click
, CLICK_DELTA = 100 // const, click -> stop
, abs = Math.abs
, clickCoordinates = []
, dispatch = ($.event.handle || $.event.dispatch)
;
$.support.touch = support;
$.event.touchStart = support ? 'touchstart' : 'mousedown';
$.event.touchEnd = support ? 'touchend' : 'mouseup';
function preventGhostClick(point){
var lastPoint = clickCoordinates[clickCoordinates.length - 1];
if( !lastPoint || lastPoint.x !== point.x || lastPoint.y !== point.y ){
clickCoordinates.push(point);
window.setTimeout(preventGhostClickShift, 2500);
}
}
function preventGhostClickShift(){
clickCoordinates.shift();
}
function getTouchXY(evt){
evt = evt.originalEvent || evt;
var touches = (evt.touches.length ? evt.touches : evt.changedTouches), touch = (touches[0] || {});
return { x: touch.clientX, y: touch.clientY };
}
if( document.addEventListener ){
document.addEventListener('click', function (evt){
for( var point, i = 0, n = clickCoordinates.length; i < n; i++ ){
point = clickCoordinates[i];
if( abs(evt.clientX - point.x) < CLICK_DELTA && abs(evt.clientY - point.y) < CLICK_DELTA ){
evt.stopPropagation();
evt.preventDefault();
}
}
}, true);
}
// Create special event "tap"
$.event.special.tap = {
setup: function (){
var startPoint = {};
function onClick(evt){
var retVal, type = evt.type;
evt.type = 'tap';
retVal = dispatch.apply(this, arguments);
evt.type = type;
return retVal;
}
function onTouchStart(evt){
var touches = evt.originalEvent.touches;
if( !/input|select|textarea/i.test(touches[0].target.nodeName) ){
startPoint = getTouchXY(evt);
$(this).bind('touchend.tapEvent', onTouchEnd);
}
}
function onTouchEnd(evt){
$(this).unbind('touchend.tapEvent', onTouchEnd);
var endPoint = getTouchXY(evt), retVal;
if( (abs(endPoint.x - startPoint.x) < TOUCH_DELTA) && (abs(endPoint.y - startPoint.y) < TOUCH_DELTA) ){
retVal = onClick.call(this, evt); // call base handler
if( (retVal !== void 0 && !retVal) || evt.isDefaultPrevented() || evt.isPropagationStopped() ){
preventGhostClick(startPoint);
}
}
return retVal;
}
// Bind listeners
$(this).bind('click.tapEvent', onClick);
if( support && !$.touchSupportDisabled ){
$(this).bind('touchstart.tapEvent', onTouchStart);
}
},
teardown: function (){
$(this).unbind('.tapEvent');
}
};
// Export as "tap" method
$.fn.tap = function (fn){
return this.bind('tap', fn);
};
})(window, jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment