Javascript Powered Webapps and Click Versus Tap

As most mobile web application developers already will know there is a very disturbing issue known as the onClick delay.

The problem in brief

Skip this section if you’re familiar with the problem.

Mobile browsers have a significant, 300ms delay before triggering a click event. So after you “clicked” a link or element that has a click handler bound to it it will take about 300ms before the click gets executed. Add some time on top of that for your javascript to do it’s magic and you might easily be experiencing a 500ms delay.

You can eliminate this delay by using touch events and adding touchstart listeners instead of click listeners but they have a nasty side-effect: After the touchstart / (touchmove) / touchend cycle a click event will still be triggered, completely isolated from the touch events.

This means that when your touchstart (or tap) listeren has done it’s magic, like changing the HTML or initiating some server interaction a click event will follow on (pretty much) the same coordinates that the touch event took place. But since your HTML already got updated this might very well be a click on some other element causing undesired “ghost” interaction with your website / web-app.

Solution 1 – clickbusters

There have been several attempts to solve this issue that are all very similar, apparently called “clickbusters”.

What clickbusters essentially do is that replace click events by touch events. When you tap somewhere they will try to determine if it’s a ghost click and prevent the handler from being executed.

Examples are:

  1. jQuery Mobile’s vmouse events – part of  jQueryMobile  - tested, not foolproof.
    jQuery Mobile offers a vmouse event binding that replaces click handlers for touch handlers if the browser supports them. When a touch event is received it records the coordinates of that events so that when a click event shows up, somewhere in it’s event handler structure, it will try to figure out if this click happened at roughly the same coords and thus is a ghost click or not. 

    Liberally quoting jQueryMobile contributor Todd Parker: “jQuery has the vclick plugin but that still doesn’t solve the problems mainly because Android’s event system is a mess ()

  2. Touch to Click – jQuery plugin – tested, not foolproof.
    You simply include this plugin into your jQuery / jQueryMobile powered project and voilá, click events have been replace by touch events.
    It is different from jQueryMobile’s vmouse in the sense that it doesn’t care about coordinates. It basically tries to capture any click that is triggered within a configurable amount of time after the touch event.
    Initially this solution seemed to be better than the vmouse approach but I realized that there were a lot of devices (both Android and iOS) that didn’t take this thing very well either. 

  3. A script by Google that does the same as jQueryMobile’s vmouse - not tested.
    A blog entry called “Creating Fast Buttons for Mobile Web Applications” on the Google Developer blog describes some code that is very similar to the before mentioned “vmouse” but does not use jQuery or jQueryMobile.

Solution 2 – disable zooming

In the Google Developer Blog article mentioned above the author makes an interesting statement:

There are already a few mobile JavaScript libraries available that solve this same problem, but we haven’t yet seen any that provide the fallback for click events or a solution to the ghost clicks problem. We hope that browser developers will solve this problem in future releases by firing click events immediately when zooming is disabled on the website (using the viewport meta tag). In fact, this is already the case with the Gingerbread Android Browser.”

Although disabling zooming might not be an option for everyone, it sounds like another solution for the native Android browser on Android 2.3 and higher is to add the following viewport meta tag:

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">

I have tried this and tested it on Google Nexus One (Android 2.3) and Google Nexus S (4.1.2) and Google Galaxy Nexus (4.1.1) but I can’t say I’ve noticed any significant difference.

Solution 3 – Be content concious

The lamest but possibly the most effective solution is to make sure that when your content changes after a tap event, that there is no clickable content in it’s place in the subsequent page.

Or you could add a semi transparent div on top of everything (completely or semi-transparent) which avoids click event to reach the underlying content.

Conclusion

There is a lot of tricks you can apply but there’s no one solution to rule them all yet.

I am very confident this issue will be solved by the browser manufacturers in the future for there is no doubt in my mind that web applications have a great future before them.