The 300 ms Click Delay and iOS 8 -Telerik Developer Network

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