FastClick: native-like tapping for touch apps

by Matthew Caruana Galizia,

This post was originally made on the Assanka blog. Assanka was acquired by the Financial Times in January 2012, and became what is now FT Labs. Learn more.

Update, August 2012: We’ve pushed a new version of FastClick to GitHub, where it’ll be hosted from now on. Details in FastClick redux.

The JavaScript APIs for handling touch events and gestures in JavaScript are simple and intuitive enough to grasp on first try, and the increasingly excellent support for web standards in Webkit browsers means we can create highly touch optimised web apps that look and feel like native apps. But that doesn’t mean there won’t be any hitches when you get deeper into web app development, and one of those hitches only becomes apparent when users stop thinking of your app as a website.

Probably because touchscreen devices weren’t nearly as popular in 1995 as they are today, JavaScript DOM events tend to reflect the mouse ‘click’ paradigm, where each event neatly corresponds to a single, deliberate click of the button. Take the onClick event, for example. How does that translate to a touchscreen device? Not easily, as it turns out.

Treating a ‘tap’ as a ‘click’ is the practical approach. On iOS at least, there’s no onTap event – onClick has been repurposed for that role. But in order to properly handle multiple-touch gestures like pinching or even double-taps, some compromises have to be made. One of these is the roughly 300ms delay between a tap and the firing of a click event, which can make your apps feel laggy and unresponsive even when it’s not technically so.

The technique we use to get around this problem is to track all TouchStart events in our app and fire a click event as soon as we receive a TouchEnd event (unless some application-specific exception is satisfied).

As we refined our fast-clicking code, we turned it into a small and efficient library, which we call FastClick. This code has been tried and tested by hundreds of thousands of users, and so far has proved to be very robust. We’d love to know what others are doing to address this challenge and find ways of improving our own approach, and to help kick off that process, we’re open-sourcing FastClick today. We’d encourage you to try it out, and let us know what you think.

To use FastClick, instantiate it on the layer you’d like to be fast-clickable – we use document.body because we want all our buttons and links to receive fast clicks. In your event listeners, ‘click’ events synthesised by FastClick will have the forwardedTouchEvent property set to true.

If you use buttons and iOS-style menus in your app then there’s a good chance your interface feels unresponsive on touchscreen devices. Here’s a simple example of how we solve that problem for a single button by instantiating FastClick on it:

<button class=”fastclick” onclick=”someHandler()”>Fast click</button>
<button onclick=”someHandler()”>Slow click
<script type='text/javascript'>
var button = document.querySelector(".fastclick");
new FastClick(button);
</script>

In the example, the button with a fastclick class will have its click handler called as soon as your finger is lifted off the screen, while tapping on any other buttons on the page will trigger the same handler after a noticeable delay. Try the live demo.

Unfortunately, the select element doesn’t behave normally when receiving (synthesised) programmatic clicks, so if you apply FastClick to an element that contains selects, FastClick will ignore clicks on the select and allow the normal click event to fire.

If you want any other elements (besides selects) in your FastClick layer to receive non-programmatic clicks, you’ll need to use one of two classes: clickevent or touchandclickevent. For any clickable element in a FastClick layer, tapping the element will cause different effects depending on how you use these classes:

  • No class: The element will receive only a programmatic click from FastClick. The default click event triggered by the user will be suppressed.
  • clickevent: The element will receive only the default click event, and will be ignored by FastClick
  • touchandclickevent: The element will receive both the default click event AND a programmatic click from FastClick (the FastClick one will be triggered first). This is only safe if your handler’s action is idempotent.

Here’s an example of all three:

<div class="fastclick">
	<button onclick="someHandler()">
		Will receive programmatic click event
	</button>
	<button class="clickevent" onclick="someHandler()">
		Will receive non-programmatic click event
	</button>
	<button class="touchandclickevent" onclick="someHandler()">
		Will receive both click events
	</button>
</div>
<script type='text/javascript'>
var button = document.querySelector(".fastclick");
new FastClick(button);
</script>

When is this useful? Try the other live demo we’ve built using a click event handler that attempts to trigger focus on an input element. iOS will only allow focus to be triggered on other elements within a handler function if the event that triggered it was non-programmatic.

We’ll be posting updates and answering questions on this blog. If the interest reaches a stage where FastClick could benefit from the developer community, we’ll move to a public repository host. But for now, concentrate on giving your users the best response time they can get – download FastClick (or minified) and give it a go. Its free to use in all your apps, and licensed under the MIT license.