The 300 ms Click Delay and iOS 8

ios_delay_header

Oh the 300 ms click delay. It’s my second favorite arcane web development problem—right behind determining which element has focus within an iframe.

If you have no idea what the 300 ms click delay is you can start by reading this guide I wrote to it last year, but here’s the tl;dr version: mobile browsers have a gesture known as double tap to zoom, and to make double tap to zoom work, browsers don’t fire a click event (or the various mouse events) until ~300 milliseconds after the user taps the screen. Simply put, without a delay browsers cannot differentiate between a “click” and the first half of a double tap.

This behavior is easy to verify with the following snippet of code that measures the time between the touchend event (which isn’t delayed) and the click event (which is):

<button>Click</button>
<p>Delay was <span></span> milliseconds.</p>
<script>
    var button = document.querySelector( "button" ),
        span = document.querySelector( "span" ),
        delay;

    button.addEventListener( "touchend", function() {
        delay = Date.now();
    });
    button.addEventListener( "click", function() {
        span.innerHTML = Date.now() - delay;
    });
</script>

The gif below shows the behavior in action on an iPhone running iOS 7 (We’ll get to iOS 8 momentarily). Notice that the delay between button clicks and the click event is consistently just over 300 milliseconds.

ios-delay

You can test this behavior yourself by running http://jsbin.com/xiculayadu on your mobile device.

So what’s the problem? Well as it turns out, humans can detect 300 milliseconds, and apps that have the delay feel sluggish. In fact, studies have shown that “0.1 seconds is about the limit for having the user feel that the system is reacting instantaneously”. So succinctly, mobile apps that don’t address the click delay feel slow.

What’s being done

The good news is that all mobile browser makers have acknowledged this problem. The bad news is they have picked three different ways of handling it.

Chrome and Firefox

Chrome and Firefox for Android disable the delay when you use a <meta name="viewport"> tag that sets the viewport’s width to the device’s width (or smaller)—for instance <meta name="viewport" content="width=device-width">.

The use of <meta name="viewport" content="width=device-width"> is a staple of responsive designs, so most responsive apps are already excluded from the click delay in Chrome and Firefox.

Here’s the same timing example running in Chrome for Android with the addition of the meta viewport tag. Notice that there is now virtually no delay between the touchstart and click events:

android-delay

Internet Explorer

Internet Explorer lets you avoid the delay with the touch-action CSS property. By setting the touch-action property on all clickable elements to none or manipulation, you can easily eliminate the click delay from your apps.

Here’s our timing example running in IE with addition of <style>a[href], button { touch-action: manipulation; }</style> to remove the click delay. Notice how, like Chrome, the delay is now essentially gone.

windows-phone-delay

Although different, both Chrome/Firefox and IE’s solutions are sane and really easy to implement. In a few minutes you can remove the 300 ms delay in three of the four big mobile browsers. Now that we’ve covered the sane solutions, let’s move on to iOS.

Update (January 7th, 2014): I mistakenly omitted that Chrome also supports touch-action since v36, and Firefox has had touch-action implemented behind a flag since v29. I refer to touch-action as IE’s solution because they were the first to implement it, and they don’t have a viewport-based solution yet (although that may change soon).

The curious case of iOS

Historically there has been no built in way to avoid the delay on iOS. Developers have suggested Chrome and IE’s solutions on the WebKit bug tracker (see tickets #122212 and #133112, respectively), but neither have been acted on.

Then iOS 8 came out. Since iOS Safari is secretly developed in a subterranean bunker, the web development community was forced to reverse engineer the browser to discover whether changes to the delay had been made. Patrick H. Lauke, who maintains a crazy awesome series of touch and pointer event tests, found that something had changed, and he, along with the web development community, eventually boiled it down to a series of heuristics. Yes, heuristics—you might want to grab some coffee.

Specifically, he found that while the delay is still in place for “fast taps”, it has been removed for “slow taps”. Stick with me.

Whether a tap is fast or slow is determined by how long your finger is on the screen, or more specifically, the difference between the touchstart and touchend events. Taps where your finger is on the screen for < ~125 milliseconds are considered fast, and the delay is maintained; taps where your finger is on the screen for > ~125 milliseconds are considered slow, and the delay is removed.

To show this in action, let’s return to the timing example. In the video below I tap the screen twice. The first tap is a slow tap, and the delay before the click event is quite small; the second tap is a fast tap, and the delay is still present.

Why would Apple do this? Fast taps are far more likely to be the first half of a double tap to zoom, so after a fast tap, Safari maintains the delay to wait for a potential second tap. A slow tap is unlikely to be part of a double tap to zoom, so Safari immediately fires the click without a delay.

I’ll let you decide whether this approach is clever or insane, but it’s important to know that these heuristics exist, and that the click delay no longer exists for slow taps in iOS 8 Safari.

Got it? Good, because we’re not done yet.

The UIWebView caveat

Just in case you weren’t confused enough, there’s one more caveat to add: the new iOS 8 heuristics are only present in iOS 8 Safari and iOS’s new WKWebView class. The heuristics are not present in the legacy UIWebView class, and they’re also not present for apps running as home screen web apps.

So does that mean your app’s behavior can vary depending on how it’s opened on iOS? Yes, yes it can. Although home screen web apps aren’t too common on iOS, UIWebView is still used a ton. The Facebook app uses it; the Twitter app uses it; heck, even Chrome for iOS is using a UIWebView under the hood.

What does this mean for my apps?

In my previous article I recommended using a library such as Kendo UI Mobile, FastClick, or Tappy! to workaround the problem for you. And if you’re using one of these libraries already you shouldn’t have to change a thing.

These libraries take a creative approach to avoid the delay altogether: they listen for touchend events instead of click events. When they detect a touchend event, they immediately create a synthetic click event using document.createEvent. Next, to avoid triggering click handlers multiple times, the libraries suppress the “real” click event.

By using a fast click library you ensure your click event handlers fire immediately without you having to worry about the plumbing to make that happen yourself. This approach has proven to work quite well, and it even functions fine in the context of the new iOS 8 heuristics. Therefore if you’re already using a fast click library of some variety you shouldn’t have to worry.

Do you need a library at all?

The more interesting question is whether you need a fast click library at all now that there are fast click solutions (of some variety) in all mobile browsers. After all, although fast click solutions work, there is a performance and maintenance cost associated with including a JavaScript library. Like all programming decisions the answer depends on your circumstances, but I would say for most web apps the answer is still yes.

Most web apps still receive a lot of views from mobile browsers without a baked in fast click solution, including the old Android browser, Chrome versions < 32, iOS 7, UIWebViews, and iOS home screen web apps. Those numbers are going to go down over time as users and developers upgrade, but at the moment, if you don’t include a fast click JavaScript library you’re leaving a lot of users with a suboptimal experience.

iOS 8 also complicates things. Even though it has a fast click “solution”, that solution is only present under certain circumstances, and using a fast click JavaScript library makes your app feel responsive regardless of whether your users are fast clickers or slow clickers.

That being said, it is unfortunate that we still need to include a JavaScript library to workaround a limitation of the mobile web. Hopefully Apple will follow in Internet Explorer or Chrome’s footsteps and implement something sane in iOS 9, but I’m not holding my breath.

Header image courtesy of Evil Erin

Comments

  • Thanks for the article! I had the exact same problem with my app DrückX. It’s a reflex game and the initial release worked with the click event, which ruined the user experience. I replaced the event handlers with touchstart and it changed everything. Take a look here: https://www.youtube.com/watch?v=bKEW02J3usA

    • Jen Looper

      hi, Sylvain, could you connect with me via Twitter @jenlooper about your game? I’m on the prowl for apps to feature on the Developer network at Telerik. Thanks!

  • Pingback: Dew Drop – January 6, 2015 (#1927) | Morning Dew()

  • Tom

    I am not sure if you are aware of https://github.com/pukhalski/tap, but it feels as natural and easy solution for this issue.

  • Rick Byers

    Chrome also supports using touch-action to remove the click delay (unlike the viewport tag, this can be used selectively to enable double-tap zoom on some parts of the UI, and fast clicking in other parts – not that that is necessarily a good idea). Firefox is getting support for touch-action too, so this is converging as the right long-term solution.

    Also you make it sound the Safari team choose this design as a solution to the click delay problem. That’s not my understanding at all – they say it’s a “byproduct”. See https://bugs.webkit.org/show_bug.cgi?id=137069#c6. I personally suspect they’ll eventually implement touch-action (or some future variant of it) as the solution for this.

    • Chrome also supports using touch-action to remove the click delay (unlike the viewport tag, this can be used selectively to enable double-tap zoom on some parts of the UI, and fast clicking in other parts – not that that is necessarily a good idea). Firefox is getting support for touch-action too, so this is converging as the right long-term solution.

      Good call. I tried to keep this article simple (so there’s also no mention of things like user-scalable), but you’re right that the wording made it seem like touch-action was an IE-only thing. I just added a note that hopefully clarifies this.

      Also you make it sound the Safari team choose this design as a solution to the click delay problem. That’s not my understanding at all – they say it’s a “byproduct”. See https://bugs.webkit.org/show_b…. I personally suspect they’ll eventually implement touch-action (or some future variant of it) as the solution for this.

      Huh. I actually had no idea that it was a “byproduct”; it had seemed fairly deliberate when I was playing around with this. I guess this goes to show you what happens when Safari doesn’t communicate their engineering decisions and leaves us to draw our own conclusions 🙂

      I’m actually really hoping that WebKit/Safari go down the touch-action path too, but as the ticket has been stagnant for 7–8 months (https://bugs.webkit.org/show_bug.cgi?id=133112) I’m not holding my breath.

      Thanks again for your comment Rick.

  • Pingback: How I Built an iOS & Android App in a Week -Telerik Developer Network()

  • Thanks for providing so much detail. It really helped us figure out what was causing an apparently ‘intermittent’ error with iPads on our application.

    Implementing an apple specific workaround was much easier.

  • mr.fusion

    There is even one more absurdity:
    > The heuristics are not present in the legacy UIWebView class
    This is not exactly correct. The heuristic is present, but only for elements, not for
    You can test it in this modified fiddle, open it in an UIWebView:
    http://output.jsbin.com/haneguzulo

  • Pingback: Developing a web app that behaves like a native app – Deadlyfingers()

  • Lorenzo Boccaccia

    beware of the synthetic clicks based on touchend, because touchendhappen before the mouseup on devices that also fire an emulated mouseup event after touches, messing up with code relying on the standard behaviour (mouseup then click) – also be aware that some devices now have both touch and mouse support, completely destroying any resemblance of sanity on the events API

  • Matt

    In what version of Kendo UI is the 300ms issue fixed – would versions as early as v2013.x still contain a fix ?

  • Florian

    There is a Fix in Webkit Nightly. But iOS Version ist still “Unspecified”.
    https://bugs.webkit.org/show_bug.cgi?id=149968

  • Rike Deodato

    Great article.. I will take a look in those fast click libaries… thanks…