HTML5 Rocks

HTML5 Rocks

A More Compatible, Smoother Touch

By Vivian Cromwell at

You and your users want mobile web apps that react and scroll smoothly to the touch. Developing them should be easy but, unfortunately, how mobile web browsers react to touch events during scrolling is left as an implementation detail in the TouchEvent specification. As a result, approaches can be broken down roughly into 4 categories. This situation exposes a fundamental tension between delivering scroll smoothness and maintaining developer control.

Four different models of touch event processing?

The behavior differences between the browsers break down into four models.

  1. Normal synchronous event processing

    Touchmove events are sent during scrolling and each scroll update blocks until touchmove handling has completed. This is good as the simplest to understand and the most powerful but bad for scroll performance because it means that each frame during the scroll must block on the main thread.

    Browsers: Android Browser (Android 4.0.4, 4.3), Mobile Safari (when scrolling div)

  2. Asynchronous touchmove processing

    Touchmove events are sent during scrolling, but scrolling can proceed asynchronously (the touchmove event is ignored after scrolling has begun). This can result in "double handling" of events, for example, continuing to scroll after the web site does something with the touchmove and calls preventDefault on the event, telling the browser not to handle it.

    Browsers: Mobile Safari (when scrolling Document), Firefox

  3. Touchmove suppressed while scrolling

    Touchmove events are not sent after scrolling starts and do not resume until after the touchend event. In this model, it's hard to tell the difference between a stationary touch and a scroll.

    Browsers: Samsung Browser (mousemove events sent)

  4. Touchcancel on scroll start

    You can't have it both ways -- scroll smoothness and developer control -- and this model makes clear the trade-off between smooth scrolling and event handling, similar to the semantics of the Pointer Events specification. Some experiences that may need to track the finger, like pull-to-refresh, are not possible.

    Browsers: Chrome Desktop M32+, Chrome Android

Why Change?

Chrome for Android currently uses Chrome's Old Model: touchcancel on scroll start, which enhances scrolling performance, but leads to developer confusion. In particular, some developers aren't aware of the touchcancel event or how to deal with it, and this has caused some web sites to break. More importantly, an entire class of UI scrolling effects and behaviors, such as pull-to-refresh, hidey bars, and snap points are difficult or impossible to implement well.

Rather than adding specifically hardcoded features to support these effects, Chrome aims to concentrate on adding platform primitives that allow developers to implement these effects directly. See A Rational Web Platform for a general exposition of this philosophy.

Chrome's New Model: The Throttled Async Touchmove Model

Chrome is introducing a new behavior designed to improve compatibility with code written for other browsers when scrolling, as well as enabling other scenarios that depend on getting touchmove events while scrolling. This feature is enabled by default and you can turn it off with the following flag, chrome://flags#touch-scrolling-mode.

The new behavior is:

  • The first touchmove is sent synchronously to allow scroll to be canceled
  • During active scrolling
    • touchmove events are sent asynchronously
    • throttled to 1 event per 200ms, or if a CSS 15px slop region is exceeded
    • Event.cancelable is false
  • Otherwise, touchmove events fire synchronously as normal when active scrolling terminates or isn't possible because the scroll limit has been hit
  • A touchend event always occurs when the user lifts their finger

You can try this demo in Chrome for Android and toggle the chrome://flags#touch-scrolling-mode flag to see the difference.

Let us know what you think

The Async Touchmove Model has the potential to improve cross-browser compatibility and enable a new class of touch gesture effects. We're interested in hearing what developers think, and in seeing the creative things you can do with it.

300ms tap delay, gone away

By Jake Archibald at

You'll find large articles throughout this site dedicated to shaving 10ms here and 90ms there in order to deliver a fast and fluid user experience. Unfortunately every touch-based mobile browser, across platforms, has an artificial ~300ms delay between you tapping a thing on the screen and the browser considering it a "click". When people think of the web as being sluggish compared to native apps on mobile, this is this one of the main contributors.

However, as of Chrome 32 for Android, which is currently in beta, this delay is gone for mobile-optimised sites, without removing pinch-zooming!

This optimisation applies to any site that uses:

<meta name="viewport" content="width=device-width">

(or any equivalent that makes the viewport <= device-width)

Why do clicks have a 300ms delay?

If you go to a site that isn't mobile optimised, it starts zoomed out so you can see the full width of the page. To read the content, you either pinch zoom, or double-tap some content to zoom it to full-width. This double-tap is the performance killer, because with every tap we have to wait to see if it might become a double-tap, and that wait is 300ms. Here's how it plays out:

  1. touchstart
  2. touchend
  3. Wait 300ms in case of another tap
  4. click

This pause applies to click events in JavaScript, but also other click-based interactions such as links and form controls.

You can't simply shortcut this with touchend listeners either. Compare these demos on a mobile browser other than Chrome 32:

Tapping on the rows changes their colour. The touchend example is much faster but also triggers after scrolling depending on the browser. This is because the spec doesn't define what can cancel the flow of touch events. Current versions of iOS Safari, Firefox, IE, and the old Android Browser trigger touchend after scrolling, Chrome doesn't.

Microsoft's PointerEvents spec does the right thing and specifies that pointerup doesn't trigger if a lower-level action such as scrolling occurs. However, currently only IE supports pointer events, although Chrome has a ticket for it. But even then, the 300ms delay would only be dropped on sites that used this listener in a way that applied to all links, form elements, and JavaScript interactions on the page.

How Chrome removed the 300ms delay

Chrome and Firefox for Android have, for some time now, removed the 300ms tap delay for pages with this:

<meta name="viewport" content="width=device-width, user-scalable=no">

Pages with this cannot be zoomed, therefore "double-tap to zoom" isn't an interaction, therefore there's no need to wait for double-taps. However, we also lose pinch-zooming.

Pinch-zooming is great for taking a closer look at a photo, some small print, or dealing with a set buttons/links that are placed too closely together. It's an out-of-the-box accessibility feature.

If a site has…

<meta name="viewport" content="width=device-width">

…double-tap zooms in a little bit. Not a particularly useful amount. A further double-tap zooms back out. We feel this feature, on mobile-optimised pages, isn't useful. So we removed it! This means we can treat taps as clicks instantly, but we retain pinch-zooming.

Is this change an accessibility concern?

We don't believe so, but the reason we release beta versions of Chrome is so users can try new features and give us feedback.

We tried to imagine a user this may affect, someone who:

  • has a motor impairment that prevents multi-touch interaction such as pinch-zoom, but not two taps in the same area within 300ms
  • has a minor visual impairment that is overcome by the small amount of zooming provided by double-tap on mobile optimised sites

But they're catered for by the text sizing tools in Chrome's settings, or the screen magnifier in Android, which covers all sites and native apps, and can be activated by triple-tap.

Chrome accessibility settingsAndroid screen magnification

However, we may have missed something, so if you are affected by this change, or know someone who is, let us know in the comments or file a ticket.

Will other browsers do the same?

I don't know, but I hope so.

Firefox has a ticket for it and currently avoids the 300ms delay for unzoomable pages.

On iOS Safari, double-tap is a scroll gesture on unzoomable pages. For that reason they can't remove the 300ms delay. If they can't remove the delay on unzoomable pages, they're unlikely to remove it on zoomable pages.

Windows phones also retain the 300ms delay on unzoomable pages, but they don't have an alternative gesture like iOS so it's possible for them to remove this delay as Chrome has. You can remove the delay using:

html {
    -ms-touch-action: manipulation;
    touch-action: manipulation;
}

Unfortunately this is a non-standard Microsoft extension to the pointer events spec. Also, programmatic fixes like this are opt-in by the developer, whereas the Chrome fix speeds up any existing mobile-optimised site.

In the mean time…

FastClick by FT Labs uses touch events to trigger clicks faster & removes the double-tap gesture. It looks at the amount your finger moved between touchstart and touchend to differentiate scrolls and taps.

Adding a touchstart listener to everything has a performance impact, because lower-level interactions such as scrolling are delayed by calling the listener to see if it event.preventDefault()s. Thankfully, FastClick will avoid setting listeners in cases where the browser already removes the 300ms delay, so you get the best of both!