/* * Author: cargomedia.ch * * Binds 'touchstart' when binding $.on('click') * and triggers 'click' when 'touchend' happens without 'touchmove' inbetween. */ (function($) { if (!("ontouchstart" in window)) { return; } var clickbuster = { isLocked: false, delayedUnlock: null, onClick: function(event) { if (this.isLocked) { event.stopPropagation(); event.preventDefault(); } }, lock: function() { this.isLocked = true; var clickbuster = this; this.delayedUnlock = setTimeout(function() { clickbuster.unlock(); }, 2000); }, unlock: function() { this.isLocked = false; if (this.delayedUnlock) { window.clearTimeout(this.delayedUnlock); } } }; document.addEventListener('click', function(e) { clickbuster.onClick(e); }, true); /** * Copied from https://github.com/ftlabs/fastclick/blob/master/lib/fastclick.js * * @param {EventTarget|Element} target Target DOM element * @returns {boolean} Returns true if the element requires a call to focus to simulate native click. */ var needsFocus = function(target) { switch (target.nodeName.toLowerCase()) { case 'textarea': case 'select': return true; case 'input': switch (target.type) { case 'button': case 'checkbox': case 'file': case 'image': case 'radio': case 'submit': return false; } // No point in attempting to focus disabled inputs return !target.disabled; default: return (/\bneedsfocus\b/).test(target.className); } }; $.event.special.click = { delegateType: "click", bindType: "click", setup: function(data, namespaces, eventHandle) { var element = this; var touchHandler = { handleEvent: function(e) { switch (e.type) { case 'touchstart': this.onTouchStart(e); break; case 'touchmove': this.onTouchMove(e); break; case 'touchend': this.onTouchEnd(e); break; } }, onTouchStart: function(e) { e.stopPropagation(); this.moved = false; element.addEventListener('touchmove', this, false); element.addEventListener('touchend', this, false); }, onTouchMove: function(e) { this.moved = true; }, onTouchEnd: function(e) { element.removeEventListener('touchmove', this, false); element.removeEventListener('touchend', this, false); if (!this.moved) { clickbuster.unlock(); var theEvent = document.createEvent('MouseEvents'); theEvent.initEvent('click', true, true); e.target.dispatchEvent(theEvent); clickbuster.lock(); e.stopPropagation(); if (!needsFocus(e.target)) { e.preventDefault(); } } } }; element.addEventListener('touchstart', touchHandler, false); $(element).data('touchToClick-handler', touchHandler); return false; }, teardown: function(namespaces) { var element = this; var touchHandler = $(element).data('touchToClick-handler'); element.removeEventListener('touchstart', touchHandler, false); return false; } }; })(jQuery);