Ghost clicks in mobile browsers

Ghost clicks are not new in mobile browsers, there is already literature on the web and some solutions to get rid of them (see resources at the end), but it is a complex problem and browsers manage them in different ways. So, let’s review the full problem and see how it is possible to address it.

What are ghost clicks ?

In any touch device, mobile browsers emulate click events when the screen is touched. It is useful to be compatible with websites developed for mouse interaction, and it is needed because there are no native touch events such as tap, swipe, double-tap, etc.
One problem is that in all these browsers, a double tap on the screen triggers a zoom in. In that case, no click event should be fired as the user is obviously not “clicking”. As a consequence, after the first tap, the browser must wait a while to be sure that a second tap for zooming is not happening.

So, here is what happens when the screen is taped:

  1. A touchstart event is fired as soon as a finger touches the screen.
  2. A touchend event is fired as soon as the finger leaves the screen.
  3. The browser waits around 300ms for another tap.
  4. If not, a click event is fired. This is the ghost click.

Issues with ghost clicks

This 300ms delay makes mobile websites to response slowly, and degrades the user experience. So developers tend to avoid listening to these click events, but instead use the touchstart and touchend events to identify user’s taps. Frameworks usually provide a gesture library to simplify this, and extend it to other touch gestures like swipe, pinch, double-tap, etc. For example with Aria Templates: http://ariatemplates.com/mobile/kitchensink/#/gestures

But the click events are still fired, and they can lead to bad situations because even if not used, they can trigger changes on the UI. For example a click on a link will start navigation, a click on an input field will give it focus and will make the virtual keyboard to pop out.

The worst part is that the click event is fired on the DOM element which is located where the touchend event happened. So if the DOM has been changed in the meantime, then a click event is fired on the new element. This is a serious problem for websites made with a client-side templating framework.
Here is a sample which illustrates this use case:  http://instant.ariatemplates.com/anonymous/73cff696276a3fd79b6f/5a1657 or http://bit.ly/1sEqWCG

Preventing ghost clicks

Most mobile browsers offer ways to prevent or to mitigate ghost clicks, but for some of them, only javascript code can solve the problem.  Some possible workarounds:

  1. Calling preventDefault() on the touchstart event completely prevents the ghost click. It works in most browsers but has a big drawback: it is no longer possible to start scrolling the page from this DOM element.
  2. Call preventDefault() on the touchend event also completely prevents the ghost click. No drawbacks here at first sight, but it is only supported by a some browsers.
  3. If the page is not scalable thanks to proper meta tags (see Trick section at the end of the article), some browsers do not wait 300ms to fire the ghost clicks. They are still fired but faster, so the click event can reliably be used instead of a tap gesture.
  4. Clickbusters, i.e. javascript code that identifies ghost clicks and cancel them. It works but it has to be implemented carefully as cancelling too much clicks will make native HTML elements to not work correctly (e.g links, checkboxes, radio buttons).

The table below illustrates the discrepancies between mobile browsers when it comes to ghost click management. All tests were done with real devices using test pages available at http://mlaval.github.io/ghostclick/

preventDefault() Ghost click timing Ghost click coordinates
touchstart touchend Scalable page Not scalable page Scalable page Not scalable page
Safari Mobile iOS 5.1.1 Yes Yes 370ms after end 370ms after end touchstart touchstart
Safari Mobile iOS 6.1.3 Yes Yes 370ms after end 370ms after end touchstart touchstart
Safari Mobile iOS 7.1.1 Yes Yes 370ms after end 370ms after end touchstart touchstart
Android 2.3.7 Yes No 410ms after end 410ms after end touchstart touchstart
Android 4.0.4 Yes No 300ms after end 10ms after end touchstart touchstart
Android 4.1.2 Yes No 300ms after end 300ms after end touchstart touchstart
Android 4.2.2 Yes No 300ms after start 10ms after end touchstart touchend
IE10 Windows Phone 8 No No 310ms after end 10ms after end touchend touchend
Blackberry 10 Yes Yes 260ms after end 10ms after end touchstart touchstart
Chrome for iOS Yes Yes 360ms after end 360ms after end touchstart touchstart
Chrome for Android Yes Yes 300ms after start 10ms after end touchstart touchend
Firefox for Android Yes No 300ms after end 10ms after end touchstart touchend

Take away

From these results, three cases can be isolated depending on the browser and on the scalability of the page:

  • Ghost click is prevented when calling preventDefault() on the touchend event.
  • Fast ghost click mode (~10ms delay)
  • Slow ghost click mode (~300ms delay)

The only universal solution is a good clickbuster, which ideally should identify which of the 3 cases is the current one, and then apply the right fix. But such an identification is difficult before the application is actually used, so an approach for a good tap gesture would be:

  • Always call preventDefault() on the touchend event.
  • After a successful tap, cancel all click events in the same area during the next 500ms.
  • Analyze the first click bustings to identify if ghost clicks are prevented (case 1) or if ghost clicks are fast (case 2) is happening. If case 1, simply stop trying to cancel clicks. If case 2, only cancel clicks during 50ms after a tap.

Resources

To go further, some articles about ghost clicks:
http://updates.html5rocks.com/2013/12/300ms-tap-delay-gone-away
http://www.appwards.nl/blog/2013/01/26/javascript-powered-webapps-and-click-versus-tap

And some clickbuster implementations:
https://github.com/ConradIrwin/zepto-ghostclick
https://developers.google.com/mobile/articles/fast_buttons
https://github.com/ftlabs/fastclick

Trick

To make a page not scalable, the following meta header must be added:
<meta name="viewport" content="width=device-width, user-scalable=no">
In addition in IE10, also add this CSS rule to remove the ghost click delay:
html {
-ms-touch-action: manipulation;
touch-action: manipulation;
}

One response on “Ghost clicks in mobile browsers

  1. dpreussner Team Member
    on 28 May 2014, 8:16 am

    Nice write up of the matter. But I still feel like HTML5, despite it recent advances, is not the right tool to deliver a first class mobile experience. The click/touch behavior for example is a non issue when doing native development…

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>