Event delegation for touch events in JavaScript

. 4 min read.

Anyone who has experience developing web applications on mobile devices will have no doubt encountered the 300ms delay when firing click events in web browsers such as Mobile Safari. There are some useful standalone plugins and informative articles to help combat this delay, but this post aims to show a super quick example of how to do simulated ‘tap’ events on dynamic content using JavaScript event delegation.

A common technique when dealing with click events in dynamic content is to use event delegation to capture events bubbling up through the DOM. This same technique can be applied to touch events, although it is a little more difficult since there are at least four different events to capture; touchstart, touchmove, touchend and touchcancel.

Here’s a very basic example of how to capture a simulated ‘tap’ event:

var tapArea, moved, startX, startY;

tapArea = document.getElementById('list'); //element to delegate
moved = false; //flags if the finger has moved
startX = 0; //starting x coordinate
startY = 0; //starting y coordinate

//touchstart
tapArea.ontouchstart = function(e) {

    moved = false;
    startX = e.touches[0].clientX;
    startY = e.touches[0].clientY;
};

//touchmove
tapArea.ontouchmove = function(e) {

    //if finger moves more than 10px flag to cancel
    //code.google.com/mobile/articles/fast_buttons.html
    if (Math.abs(e.touches[0].clientX - startX) > 10 ||
        Math.abs(e.touches[0].clientY - startY) > 10) {
            moved = true;
    }
};

//touchend
tapArea.ontouchend = function(e) {

    e.preventDefault();

    //get element from touch point
    var element = e.changedTouches[0].target;

    //if the element is a text node, get its parent.
    if (element.nodeType === 3) {
        element = element.parentNode;
    }

    if (!moved) {
        //check for the element type you want to capture
        if (element.tagName.toLowerCase() === 'label') {
            console.log('tap!');
        }
    }
};

//don't forget about touchcancel!
tapArea.ontouchcancel = function(e) {

    //reset variables
    moved = false;
    startX = 0;
    startY = 0;
};

Notice here it is useful to make use of touchmove to detect any finger dragging gestures that might occur after a touchstart has fired. This way it is easy to cancel the ‘tap’ on touchend if desired.

Update: Originally this article was using the function document.elementFromPoint(x,y) to get the element target in ontouchend. A few people have kindly pointed out that you can actually just use e.changedTouches[0].target or even e.target to get the same result for a simple ‘tap’. It should be noted however, that the target in this case always refers to the originating element, so if you do need to reference an element a finger might have moved on/off during touchmove or touchend, you would still need to use:

document.elementFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY);

Life would be so much easier if we had a native ‘tap’ event in mobile browsers!