JSFiddle - React, Tailwind, and code Playground
by Evan Sharp
HTML
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></script>
<div data-ng-app="myApp" ng-controller="ctrl">
<button id="toggleAll" data-ng-click="toggleAll()">Toggle All</button>
<div ng-repeat="item in items track by $index">
<button id="toggle{{$index}}" data-ng-click="items[$index] = !items[$index]">Toggle</button>
<div id="box-thing" data-off-click="items[$index] = false" data-off-click-if="items[$index]" data-off-click-filter="'#toggleAll,#toggle'+$index" data-ng-show="items[$index]">Clicking outside of this will close this</div>
</div>
</div>
CSS
#toggle {
padding: 5px 10px;
margin: 10px;
}
#box-thing {
padding: 5px;
border: 2px solid #ccc;
}
JavaScript
angular.module('offClick', [])
.directive('offClick', ['$document', '$rootScope', '$parse', function ($document, $rootScope, $parse) {
var id = 0;
var listeners = {};
$document.on('click', offClickEventHandler);
function targetInFilter(target, elms) {
if (!target || !elms) return false;
var elmsLen = elms.length;
for (var i = 0; i < elmsLen; ++i)
if (elms[i].contains(target)) return true;
return false;
}
function offClickEventHandler(event) {
if (event.pageX == 0 && event.pageY == 0) return;
var callbacks = [];
var target = event.target || event.srcElement;
angular.forEach(listeners, function (listener, i) {
if (!(listener.elm.contains(target) || targetInFilter(target, listener.offClickFilter))) {
callbacks.push(function () {
listener.cb(listener.scope, {
$event: event
});
});
}
});
$rootScope.$evalAsync(function () {
angular.forEach(callbacks, function (callback, i) {
callback();
})
});
}
return {
restrict: 'A',
compile: function ($element, attr) {
var fn = $parse(attr.offClick);
return function (scope, element) {
var elmId = id++;
var offClickFilter;
if (attr.offClickIf) {
$rootScope.$watch(function () {
return $parse(attr.offClickIf)(scope);
}, function (newVal) {
if (newVal) {
on();
} else if (!newVal) {
off();
}
});
} else {
on();
}
attr.$observe('offClickFilter', function (value) {
...