AngularJS Directive for responsive clicks in mobile web apps

If you’ve ever done any web development specifically for mobile devices, you may have noticed that clicks don’t always seem responsive.  This is probably because the device takes extra time to determine whether or not the user is trying to click a button on the web page or to scroll the page.

Fortunately, the touch events in javascript are registered instantly.  This means we can listen to the touch events and kludge up our own click event.  I found this idea here and if you’re not using angularjs, then you may just want to use that code instead.

Here’s the code already, and I’ll try to explain some of the details down below:

AppRoot.directive('fastClick', ['$parse', function ($parse) {
    return function (scope, element, attr) {
        var fn = $parse(attr['fastClick']);
        var initX, initY, endX, endY;
        var elem = element;
        var maxMove = 4;

        elem.bind('touchstart', function (event) {
            event.preventDefault();
            initX = endX = event.touches[0].clientX;
            initY = endY = event.touches[0].clientY;
            elem.bind('touchend', onTouchEnd);
            elem.bind('touchmove', onTouchMove);
        });

        function onTouchMove(event) {
            endX = event.touches[0].clientX;
            endY = event.touches[0].clientY;
        };

        function onTouchEnd(event) {
            elem.unbind('touchmove');
            elem.unbind('touchend');
            if (Math.abs(endX - initX) > maxMove) return;
            if (Math.abs(endY - initY) > maxMove) return;
            scope.$apply(function () { fn(scope, { $event: event }); });
        };
    };
} ]);

Here is a fiddle to see it in action: http://jsfiddle.net/7Q8r2/

Pull that up on your ipad and you’ll see that the difference is very noticeable. It also stops the page from zooming if you double click.

The click isn’t fired off until the finger is actually lifted. This is just in case a user actually wants to scroll instead of click. The init and end variables are used to track whether or not the touch moved and by how much. I added a maxMove variable so that the finger can move a little and still have it behave like a click. I set it to 4 for now with no real reason other than it was slightly larger than 0.

There’s still a little room for improvement.  If you wanted to implement a ‘depressed’ look for a button, you could modify the element css within the touchStart and touchEnd events. You could also detect whether or not the touch events exists, then bind to either the ‘click’ or ‘touchstart’ event, then it would work in normal browsers, too.

  • paul

    This works pretty good in iPad but I encountered problem in Android tablet. It is running Android 4.0.4 and fastclick dose not work with latest Chrome or Firefox android. Normal click work

  • steve

    Doesn’t work on iPad 2