Skip to content

Commit 6990040

Browse files
adds global filter option to off-click-filter
1 parent f8d12a5 commit 6990040

8 files changed

Lines changed: 159 additions & 4 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,8 @@ Here we have a slide out navigation div that will appear when the user clicks a
3535
```
3636

3737
The `off-click` attribute is the expression or function that will execute each time the user doesn't click on your element (or filter)<br />
38+
3839
The optional `off-click-if` attribute is an expression that will determine if the `off-click` should trigger or not.<br/>
39-
The optional `off-click-filter` directive allows you to pass a comma separated list of targets whose `off-click` will not be triggered when the element `off-click-filter` was applied to is clicked (gets parsed as javascript, so remember to wrap in single quotes)
40+
41+
The included `off-click-filter` directive allows you to pass a comma separated list of targets whose `off-click` will not be triggered when the element `off-click-filter` was applied to is clicked (gets parsed as javascript, so remember to wrap in single quotes).
42+
If you pass `off-click-filter="'*'"` that element will be a filter for every off-click on the page.

dist/angular-off-click.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ angular.module('offClick').directive('offClick', ["$rootScope", "$parse", "OffCl
4747
}
4848
var target = event.target || event.srcElement;
4949
angular.forEach(listeners, function (listener, i) {
50-
var filters = [];
50+
var filters = OffClickFilterCache['*'] || [];
5151
if (listener.elm.id && listener.elm.id !== '') {
5252
if (OffClickFilterCache['#' + listener.elm.id]) filters = filters.concat(OffClickFilterCache['#' + listener.elm.id]);
5353
}

dist/angular-off-click.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/angular-off-click.module.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
angular.module('offClick', []);

dist/directives/offClick.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
'use strict';
2+
3+
angular.module('offClick').directive('offClick', ["$rootScope", "$parse", "OffClickFilterCache", function ($rootScope, $parse, OffClickFilterCache) {
4+
var id = 0;
5+
var listeners = {};
6+
// add variable to detect touch users moving..
7+
var touchMove = false;
8+
9+
var targetInFilter = function targetInFilter(target, elms) {
10+
if (!target || !elms) return false;
11+
var elmsLen = elms.length;
12+
for (var i = 0; i < elmsLen; ++i) {
13+
var currentElem = elms[i];
14+
var containsTarget = false;
15+
try {
16+
containsTarget = currentElem.contains(target);
17+
} catch (e) {
18+
// If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
19+
// see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
20+
// In this case we use compareDocumentPosition() instead.
21+
if (typeof currentElem.compareDocumentPosition !== 'undefined') {
22+
containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
23+
}
24+
}
25+
26+
if (containsTarget) {
27+
return true;
28+
}
29+
}
30+
return false;
31+
};
32+
33+
var offClickEventHandler = function offClickEventHandler(event) {
34+
// If event is a touchmove adjust touchMove state
35+
if (event.type === 'touchmove') {
36+
touchMove = true;
37+
// And end function
38+
return false;
39+
}
40+
// This will always fire on the touchend after the touchmove runs...
41+
if (touchMove) {
42+
// Reset touchmove to false
43+
touchMove = false;
44+
// And end function
45+
return false;
46+
}
47+
var target = event.target || event.srcElement;
48+
angular.forEach(listeners, function (listener, i) {
49+
var filters = [];
50+
if (listener.elm.id && listener.elm.id !== '') {
51+
if (OffClickFilterCache['#' + listener.elm.id]) filters = filters.concat(OffClickFilterCache['#' + listener.elm.id]);
52+
}
53+
// classList is an object in IE10 and 11 iirc, using angular.forEach to iterate both over an array or object values
54+
angular.forEach(listener.elm.classList, function (className) {
55+
if (OffClickFilterCache['.' + className]) filters = filters.concat(OffClickFilterCache['.' + className]);
56+
});
57+
if (!(listener.elm.contains(target) || targetInFilter(target, filters))) {
58+
$rootScope.$evalAsync(function () {
59+
listener.cb(listener.scope, {
60+
$event: event
61+
});
62+
});
63+
}
64+
});
65+
};
66+
67+
// Add event listeners to handle various events. Destop will ignore touch events
68+
document.addEventListener("touchmove", offClickEventHandler, true);
69+
document.addEventListener("touchend", offClickEventHandler, true);
70+
document.addEventListener('click', offClickEventHandler, true);
71+
72+
return {
73+
restrict: 'A',
74+
compile: function compile(elem, attrs) {
75+
var fn = $parse(attrs.offClick);
76+
77+
var elmId = id++;
78+
var removeWatcher = void 0;
79+
80+
var on = function on() {
81+
listeners[elmId] = {
82+
elm: element[0],
83+
cb: fn,
84+
scope: scope
85+
};
86+
};
87+
88+
var off = function off() {
89+
listeners[elmId] = null;
90+
delete listeners[elmId];
91+
};
92+
93+
if (attrs.offClickIf) {
94+
removeWatcher = $rootScope.$watch(function () {
95+
return $parse(attrs.offClickIf)(scope);
96+
}, function (newVal) {
97+
newVal && on() || !newVal && off();
98+
});
99+
} else on();
100+
101+
return function (scope, element) {
102+
scope.$on('$destroy', function () {
103+
off();
104+
if (removeWatcher) {
105+
removeWatcher();
106+
}
107+
element = null;
108+
});
109+
};
110+
}
111+
};
112+
}]);

dist/directives/offClickFilter.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
angular.module('offClick').directive('offClickFilter', ["OffClickFilterCache", "$parse", function (OffClickFilterCache, $parse) {
4+
var filters = void 0;
5+
var addFiltersToCache = function addFiltersToCache(filters) {
6+
filters.forEach(function (filter) {
7+
OffClickFilterCache[filter] ? OffClickFilterCache[filter].push(elem[0]) : OffClickFilterCache[filter] = [elem[0]];
8+
});
9+
};
10+
return {
11+
restrict: 'A',
12+
compile: function compile(elem, attrs) {
13+
addFiltersToCache($parse(attrs.OffClickFilterCache).split(',').map(function (x) {
14+
return x.trim();
15+
}));
16+
return function (scope, element) {
17+
scope.$on('$destroy', function () {
18+
element = null;
19+
filters.forEach(function (filter) {
20+
if (OffClickFilterCache[filter].length > 1) {
21+
OffClickFilterCache[filter].splice(OffClickFilterCache[filter].indexOf(elem[0]), 1);
22+
} else {
23+
OffClickFilterCache[filter] = null;
24+
delete OffClickFilterCache[filter];
25+
}
26+
});
27+
});
28+
};
29+
}
30+
};
31+
}]);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict';
2+
3+
angular.module('offClick').factory('OffClickFilterCache', function () {
4+
var filterCache = {};
5+
return filterCache;
6+
});

src/directives/offClick.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ angular.module('offClick')
4545
}
4646
const target = event.target || event.srcElement;
4747
angular.forEach(listeners, (listener, i) => {
48-
let filters=[];
48+
let filters = OffClickFilterCache['*'] || [];
4949
if(listener.elm.id && listener.elm.id !== '') {
5050
if(OffClickFilterCache['#' + listener.elm.id]) filters = filters.concat(OffClickFilterCache['#'+listener.elm.id]);
5151
}

0 commit comments

Comments
 (0)