Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
  <head>
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body data-ng-app="myApp">
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
    <h1>Hello Plunker!</h1>
    <div data-ng-controller="myAppController">
      <button data-ng-click="toggleContainer()">Toggle Container</button>
      <div class="container" data-ng-show="showContainer" data-off-click="hideContainer($event)" data-off-click-if="showContainer">
        This is the container
      </div>
    </div>
  </body>
</html>
 
.container {
  background: blue;
  color: #fff;
  height: 300px;
  width: 300px;
}
 
// Angular App Code
var app = angular.module('myApp', ['offClick']);
app.controller('myAppController', ['$scope', '$timeout', function($scope,$timeout) {
  $scope.showContainer = false;
  
  $scope.toggleContainer = function() {
    $timeout(function() {
      $scope.showContainer = !$scope.showContainer;
    }, 300);
  };
  
  $scope.hideContainer = function($event) {
    $event.stopPropagation();
    $timeout(function(){
      $scope.showContainer = false;  
    });
  };
}]);
// Off Click Directive Code
angular.module('offClick', [])
    .directive('offClick', ['$rootScope', '$parse', function ($rootScope, $parse) {
    var id = 0;
    var listeners = {};
    // add variable to detect touch users moving..
    var touchMove = false;
    // Add event listeners to handle various events. Destop will ignore touch events
    document.addEventListener("touchmove", offClickEventHandler, true);
    document.addEventListener("touchend", offClickEventHandler, true);
    document.addEventListener('click', offClickEventHandler, true);
    function targetInFilter(target, elms) {
        if (!target || !elms) return false;
        var elmsLen = elms.length;
        for (var i = 0; i < elmsLen; ++i) {
            var currentElem = elms[i];
            var containsTarget = false;
            try {
                containsTarget = currentElem.contains(target);
            } catch (e) {
                // If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
                // see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
                // In this case we use compareDocumentPosition() instead.
                if (typeof currentElem.compareDocumentPosition !== 'undefined') {
                    containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
                }
            }
            if (containsTarget) {
                return true;
            }
        }
        return false;
    }
    function offClickEventHandler(event) {
        // If event is a touchmove adjust touchMove state
        if( event.type === 'touchmove' ){
            touchMove = true;
            // And end function
            return false;
        }
        // This will always fire on the touchend after the touchmove runs...
        if( touchMove ){
            // Reset touchmove to false
            touchMove = false;
            // And end function
            return false;
        }
        var target = event.target || event.srcElement;
        angular.forEach(listeners, function (listener, i) {
            if (!(listener.elm.contains(target) || targetInFilter(target, listener.offClickFilter))) {
                //$rootScope.$evalAsync(function () {
                    listener.cb(listener.scope, {
                        $event: event
                    });
                //});
            }
        });
    }
    return {
        restrict: 'A',
        compile: function ($element, attr) {
            var fn = $parse(attr.offClick);
            return function (scope, element) {
                var elmId = id++;
                var offClickFilter;
                var removeWatcher;
                offClickFilter = document.querySelectorAll(scope.$eval(attr.offClickFilter));
                if (attr.offClickIf) {
                    removeWatcher = $rootScope.$watch(function () {
                        return $parse(attr.offClickIf)(scope);
                    }, function (newVal) {
                        if (newVal) {
                            on();
                        } else if (!newVal) {
                            off();
                        }
                    });
                } else {
                    on();
                }
                attr.$observe('offClickFilter', function (value) {
                    offClickFilter = document.querySelectorAll(scope.$eval(value));
                });
                scope.$on('$destroy', function () {
                    off();
                    if (removeWatcher) {
                        removeWatcher();
                    }
                    element = null;
                });
                function on() {
                    listeners[elmId] = {
                        elm: element[0],
                        cb: fn,
                        scope: scope,
                        offClickFilter: offClickFilter
                    };
                }
                function off() {
                    listeners[elmId] = null;
                    delete listeners[elmId];
                }
            };
        }
    };
}]);
Output 300px

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
moshfeupro
0viewers