An Angular 'Any Other Click' Directive
I recently implemented a very plain popup menu in Angular. Showing the menu was simple, as you would expect, but dismissing the menu threw me for a bit of a loop. How could I have my menu respond to a click event outside of the element hosting its directive? After a bit of thought, I wrote the following ‘any other click’ directive. Basically, the directive registers a click event on the document
element. This click event checks to see if it was generated from the element that registered the directive. If not, it executes the code specified in the directive attribute. jQuery is required since angular.element.find()
is limited to lookups by tag name.
Directive Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(function () {
var module = angular.module('anyOtherClick', []);
var directiveName = "anyOtherClick";
module.directive(directiveName, ['$document', "$parse", function ($document, $parse) {
return {
restrict: 'A',
link: function (scope, element, attr, controller) {
var anyOtherClickFunction = $parse(attr[directiveName]);
var documentClickHandler = function (event) {
var eventOutsideTarget = (element[0] !== event.target) && (0 === element.find(event.target).length);
if (eventOutsideTarget) {
scope.$apply(function () {
anyOtherClickFunction(scope, {});
});
}
};
$document.on("click", documentClickHandler);
scope.$on("$destroy", function () {
$document.off("click", documentClickHandler);
});
},
};
}]);
})();
Plunker Demo
Items of Interest
- Angular exposes an
$event
object duringng-click
. We use$event.stopPropagation()
in theng-click
expression that shows the popup to prevent the popup’sany-other-click
expression from firing and immediately hiding the popup. - As noted above, jQuery is required since
angular.element.find()
is limited to lookups by tag name. Alternative pure-Angular suggestions would be greatly appreciated.
Disclaimer
The code above is working for me in production. However, I only use this directive a few times on my pages. As with most things in Angular, you’d need to re-evaluate this directive’s performance if it is used in a large ng-repeat
or other looping constructs.
Update - 2015.01.07
I have updated the directive to avoid creating an isolate scope based on the ng-click
source. This lets the directive play nice with other angular directives.
comments powered by Disqus