AngularJS - Click & Tap Directives

by gavinfoley

HTML

<div ng-app="AppRoot">
    <div ng-controller="DemoCtrl">
        <div fast-click="fastclickout = 'fastclick ' + fastclickout" class="button">Fast Click</div>
        <div>{{fastclickout}}</div>
        <div ng-click="normalclickout = 'normalclick ' + normalclickout" class="button">Normal Click</div>
        <div>{{normalclickout}}</div>
        <div tap-or-click="taporclickout = 'taporclick ' + taporclickout" class="button">Tap Or Click</div>
        <div>{{taporclickout}}</div>
    </div>
</div>

CSS

.button {
    padding:5px;
    background-color:#cccccc;
    border-radius:4px;
    width:100px;
    cursor: pointer;
}
div {
    margin: 20px;
}

JavaScript

var AppRoot = angular.module('AppRoot', []);

AppRoot.controller('DemoCtrl', ['$scope', function (scope) {
    scope.fastclickout = '';
}]);

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
                });
            });
        };
    };
}]);


AppRoot.directive("tapOrClick", [function () {
    return function (scope, element, attrs) {
        var tapped;
        tapped = false;
        element.bind("click", function () {
            if (!tapped) {
                return scope.$apply(attrs["tapOrClick"]);
            }
        });
        element.bind("touchstart", function (event) {
            return tapped = true;
        });
        element.bind("touchmove", function (event) {
            tapped = false;
            return event.stopImmediatePropagation();
        });
        return element.bind("touchend", function () {
            if (tapped) {
                return scope.$apply(attrs["tapOrClick"]);
            }
        });
    };
}]);