问题
I'm using the following directive to detect when a click is made outside a div:
app.directive('clickOut', function ($window, $parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var clickOutHandler = $parse(attrs.clickOut);
angular.element($window).on('click', function (event) {
if (element[0].contains(event.target)) return;
clickOutHandler(scope, {$event: event});
scope.$apply();
});
}
};
});
In this div:
<div class="panel-body" click-out="closeMyPopup()">
<div class="row clearfix">
<div class="col-md-12">
<div class="form-inline pull-right">
<button type="button" class="form-control btn" ng-click="onCancelAnnouncement()">Cancel</button>
<button type="submit" class="form-control btn" ng-click="onSaveAnnouncement()">Save</button>
</div>
</div>
</div>
</div>
It works well, when you click outside the div, the function closeMyPopup() is triggered, the issue is that the div has buttons that triggers other functions. By some reason when other function is called, (like when the buttons are clicked) the event click outside is triggered calling the closeMyPopup(), the buttons are inside the div so the event click outside should not be called. There's another directive that I can use, that has the correct behavior and not trigger the click outside when you fire another function? Or how can I workaround this?
I also use this other directive, with the same issue:
app.directive("outsideClick", ['$document', '$parse', function ($document, $parse) {
return {
link: function ($scope, $element, $attributes) {
var scopeExpression = $attributes.outsideClick,
onDocumentClick = function (event) {
var isChild = $element.find(event.target).length > 0;
if (!isChild) {
$scope.$apply(scopeExpression);
}
};
$document.on("click", onDocumentClick);
$element.on('$destroy', function () {
$document.off("click", onDocumentClick);
});
}
}
}]);
回答1:
Its because the event is being propagated to Window
object.
- Window
- document
- dialog
- button
In the above hierarchy, if a click event happens on the last button
element, the event will be propagated to the parent element until it reaches Window and then will close your dialog.
Solution 1:
Stop event propagation in each controller function by passing the event as a parameter and calling event.stopPropagation()
function:
<button type="button" class="form-control btn" ng-click="onCancelAnnouncement($event)">Cancel</button>
...
$scope.onCancelAnnouncement($event) {
$event.stopPropagation();
}
Solution 2:
Let the event be propagated and check the target element:
angular.element($window).on('click', function (event) {
var target = $(event.target);
if(target.attr("id") === "anyid") { // or target.hasClass("someclass")
// or target.closest(".some-selector")
// just ignore
} else {
// Do whatever you need
}
});
回答2:
Exactly: events will be presented to every object in the nested DOM-hierarchy unless and until you stop their propagation. This, of course, is by design: JavaScript doesn't assume that "the innermost guy I can find who's listening for this event" is the only guy who might be interested in it. Everyone who says he's listening for it, who is in the position to hear it, is going to hear it, each in their turn ... unless one of them explicitly quashes further propagation, at which JS will stop looking for anyone else to send it to. (No one has to "send the event to the outer container." Instead, they only have to tell JS not to send it on.)
来源:https://stackoverflow.com/questions/31210765/click-outside-div-when-div-has-buttons-that-fires-events-angularjs