I\'m working on a mobile web site that has to work on a variety of devices. The one\'s giving me a headache at the moment are BlackBerry.
We need to support both key
In my case this worked perfectly:
jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
jQuery(this).off('mouseup');
}
});
The main problem was when instead mouseup I tried with click, on touch devices triggered click and touchend at the same time, if i use the click off, some functionality didn't worked at all on mobile devices. The problem with click is that is a global event that fire the rest of the event including touchend.
Here's a simple way to do it:
// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) {
// Do your stuff here
}
});
You basically save the first event type that is triggered to the 'trigger' property in jQuery's data object that is attached to the root document, and only execute when the event type is equal to the value in 'trigger'. On touch devices, the event chain would likely be 'touchstart' followed by 'click'; however, the 'click' handler won't be executed because "click" doesn't match the initial event type saved in 'trigger' ("touchstart").
The assumption, and I do believe it's a safe one, is that your smartphone won't spontaneously change from a touch device to a mouse device or else the tap won't ever register because the 'trigger' event type is only saved once per page load and "click" would never match "touchstart".
Here's a codepen you can play around with (try tapping on the button on a touch device -- there should be no click delay): http://codepen.io/thdoan/pen/xVVrOZ
I also implemented this as a simple jQuery plugin that also supports jQuery's descendants filtering by passing a selector string:
// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
var selector, handler;
switch (typeof arg1) {
case 'function':
selector = null;
handler = arg1;
break;
case 'string':
selector = arg1;
if (typeof arg2==='function') handler = arg2;
else return;
break;
default:
return;
}
this.on('click touchstart', selector, function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
});
};
Codepen: http://codepen.io/thdoan/pen/GZrBdo/
Another implementation for better maintenance. However, this technique will also do event.stopPropagation (). The click is not caught on any other element that clicked for 100ms.
var clickObject = {
flag: false,
isAlreadyClicked: function () {
var wasClicked = clickObject.flag;
clickObject.flag = true;
setTimeout(function () { clickObject.flag = false; }, 100);
return wasClicked;
}
};
$("#myButton").bind("click touchstart", function (event) {
if (!clickObject.isAlreadyClicked()) {
...
}
}
I had to do something similar. Here is a simplified version of what worked for me. If a touch event is detected, remove the click binding.
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click');
//your code here
});
In my case the click event was bound to an <a>
element so I had to remove the click binding and rebind a click event which prevented the default action for the <a>
element.
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click').on('click', function(e){ e.preventDefault(); });
//your code here
});
I am trying this and so far it works (but I am only on Android/Phonegap so caveat emptor)
function filterEvent( ob, ev ) {
if (ev.type == "touchstart") {
ob.off('click').on('click', function(e){ e.preventDefault(); });
}
}
$('#keypad').on('touchstart click', '.number, .dot', function(event) {
filterEvent( $('#keypad'), event );
console.log( event.type ); // debugging only
... finish handling touch events...
}
I don't like the fact that I am re-binding handlers on every touch, but all things considered touches don't happen very often (in computer time!)
I have a TON of handlers like the one for '#keypad' so having a simple function that lets me deal with the problem without too much code is why I went this way.
Just for documentation purposes, here's what I've done for the fastest/most responsive click on desktop/tap on mobile solution that I could think of:
I replaced jQuery's on
function with a modified one that, whenever the browser supports touch events, replaced all my click events with touchstart.
$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
on: (function(){
var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
return function( types, selector, data, fn, one ) {
if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
return this._on( types, selector, data, fn);
};
}()),
});
Usage than would be the exact same as before, like:
$('#my-button').on('click', function(){ /* ... */ });
But it would use touchstart when available, click when not. No delays of any kind needed :D