Tech and Media Labs
This site uses cookies to improve the user experience.




Touch Event Handling in JavaScript

Jakob Jenkov
Last update: 2014-06-15

Touch Event Listeners

Handling touch events in JavaScript is done by adding touch event listeners to the HTML elements to handle touch events for. This is done similarly to adding a click listener:

var theElement = document.getElementById("theElement");

theElement.addEventListener("touchend", handlerFunction, false);

function handlerFunction(event) {
}

Touch events are somewhat more complex than mouse events. Like with a mouse you can listen for touch down, touch move, touch end etc. but a user only has one mouse pointer, whereas a user may touch the screen with multiple fingers at the same time.

You can listen for the following touch events:

EventDescription
touchstart Fired when the user puts one or more fingers on the screen.
touchmove Fired while the user moves the finger(s) over the screen while touching it.
touchend Fired when the user stops touching the screen.
touchleave Fired when the user moves their fingers outside of the area listening for touch events.
touchcancel Fired when a touch gesture is canceled, for instance if the user moves her fingers outside of the screen itself.

Not all browsers may fire all of these events (I have had problems getting Chrome to fire touch leave events).

Handling Taps

A tap is like a mouse click. The user taps a button or link just like they would click it with a mouse.

Mobile browsers will typically convert a tap on a button, link etc. to a mouse click after 300 milliseconds (ms), to make regular web apps work, even if the web apps is not directly listening for touch events. The browser waits 300ms to see if the user performs any more advanced touch gestures, before firing the click event. This is done to make sure that it is actually a click event that should get fired.

These 300ms makes your web app feel laggy compared to native apps. Web apps already have a disadvantage compared to native apps, and those 300ms just makes that worse. Therefore we want to listen for touch events in touch enabled browsers.

It is not enough to just listen for touch events though. You want your app to be usable in desktop browsers too (which are not touch enabled), so you have to listen for both click and touch events. Here is how that is done:

var theElement = document.getElementById("theElement");

theElement.addEventListener("mouseup", tapOrClick, false);
theElement.addEventListener("touchend", tapOrClick, false);

function tapOrClick(event) {
   //handle tap or click.

    event.preventDefault();
    return false;
}

When you listen for both touch and click events, you have a slight problem in touch enabled browsers. Even if a touch event is fired immediately after a tap, an additional click event is still fired after 300ms. That means that your listener function will get called twice!

To avoid the double calls to the listener function, you call the event.preventDefault() function on the event object. This disables the browsers default behaviour, and lets your code handle the touch event. This disables the firing of the click event 300ms after the touch event. It also disables the default action of a tap, whatever action the browser might have as default for a tap.

In a browser without touch support the touchend event never fires. Only the mouseup event will fire. Thus, your listener code will still work. It is also common in click listeners (mouseup) to disable the browsers default action for the click. For instance, if you click a submit button inside a form, the browsers default action is to submit the form. We don't want that either. Thus, it is just fine to prevent the browser's default actions from a click listener too.

If you need different handling of touch and click events, you can just implement two different event listeners, and make each handler do its work the way it needs to.

Handling Touches

As mentioned earlier, the user may use multiple fingers when touching a mobile device. Therefore touch events may contain information about more than one touche. The touch information is stored inside the event.changedTouches property of the event object. Here is how you access them:

function touchHandler(event) {
    var touches = event.changedTouches;

    for(var i=0; i < event.changedTouches.length; i++) {
        var touchId = event.changedTouches[i].identifier;
        var x       = event.changedTouches[i].pageX;
        var y       = event.changedTouches[i].pageY;
    }

}

As you can see, each touch has its own identifier, x and y coordinate.

The identifier is used to identify each touch event between events being fired. For instance, if the user touches the screen and then moves the finger (like you do when "swiping" the screen), you can track the changes in the touch via the touch identifier. You will need to copy the touch information in the touchstart event, and then match against that in the touchmove and / or touchend events. Here is an example that shows how to do that:



theElement.addEventListener("touchstart", touchStartHandler, false);
theElement.addEventListener("touchend", touchEndHandler, false);


var touchesInAction = {};

function touchStartHandler(event) {
    var touches = event.changedTouches;

    for(var j = 0; j < touches.length; j++) {

         /* store touch info on touchstart */
         touchesInAction[ "$" + touches[j].identifier ] = {

            identifier : touches[j].identifier,
            pageX : touches[j].pageX,
            pageY : touches[j].pageY
         };
    }
}

function touchEndHandler(event) {
    var touches = event.changedTouches;

    for(var j = 0; j < touches.length; j++) {

        /* access stored touch info on touchend */
        var theTouchInfo = touchesInAction[ "$" + touches[j].identifier ];
        theTouchInfo.dx = touches[j].pageX - theTouchInfo.pageX;  /* x-distance moved since touchstart */
        theTouchInfo.dy = touches[j].pageY - theTouchInfo.pageY;  /* y-distance moved since touchstart */
    }

    /* determine what gesture was performed, based on dx and dy (tap, swipe, one or two fingers etc. */

}

Notice how the touchstart event handler function copies the information from the touch objects in the event object. Some browsers may reuse these touch objects, so simply storing the original touch object may not work. The values in it might be overridden by later touches.

The touchend event handler function accesses the copied touch object and calculates how far each touch has moved since it started (dx, dy).

In order to respond to gestures like pinching, you may have to do the dx, dy calculation in the touchmove event instead of the touchend event. When reacting to a pinch the web app should react during pinching, not when the pinch ends.

In order to respond to more advanced multi touch gestures, you may have to mark each touch object copy with status flags like "ongoing" / "ended", and perhaps the time it ended. When a touchend event is fired, you can then iterate all the touch copies to see if there are still ongoing touches. If no more touches are ongoing, iterate all the touches and see which finished within the last 500ms - 1000ms. Those touches were probably all part of the last gesture. Now you can start calculating what gesture was made, based on dx, dy etc.

For really advanced gestures, like moving in a circle, you may even need to collect the whole touch path as an array during touchmove. Thus you have all the touch coordinates of the whole movement available when the touch ends.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC