点击到处,但在这里事件

想知道我将如何执行“点击任何地方,但在这个元素”事件。

我有一些你可以比较文件浏览器中的文件列表。 您可以select某些元素,但是如果在元素控制器外部单击,则需要取消select所有元素。

在这里输入图像说明

添加了一个截图,使其更清晰。 所以我想要做的是,如果我点击任何地方,但在语言元素,它应该触发一个事件。

更新

澄清我不问我怎么可以用jQuery做到这一点。

编辑:在这个旧的,旧的答案有几个问题。

*另外:标记社区Wiki(对我来说没有积分),因为错误

  1. N要求对指令进行N次使用。 对于使用匹配expression式的相同范围内的使用,这可能不是理想的。

  2. 没有什么是撕毁事件处理者! 坏! 坏! 坏!

所以,我正在更新这个答案。 希望它不会造成任何麻烦。

更新了答案

这里有一个新的解决scheme,解决了这些问题 ……个别应用程序开发人员可能会遇到其他问题。 这只是一个如何处理这个问题的例子。

app.factory('clickAnywhereButHereService', function($document){ var tracker = []; return function($scope, expr) { var i, t, len; for(i = 0, len = tracker.length; i < len; i++) { t = tracker[i]; if(t.expr === expr && t.scope === $scope) { return t; } } var handler = function() { $scope.$apply(expr); }; $document.on('click', handler); // IMPORTANT! Tear down this event handler when the scope is destroyed. $scope.$on('$destroy', function(){ $document.off('click', handler); }); t = { scope: $scope, expr: expr }; tracker.push(t); return t; }; }); app.directive('clickAnywhereButHere', function($document, clickAnywhereButHereService){ return { restrict: 'A', link: function(scope, elem, attr, ctrl) { var handler = function(e) { e.stopPropagation(); }; elem.on('click', handler); scope.$on('$destroy', function(){ elem.off('click', handler); }); clickAnywhereButHereService(scope, attr.clickAnywhereButHere); } }; }); 

原始答案(修复了事件处理程序的拆解)

你已经接近你find的一个答案,但我已经为你展示了什么是缺失的。

 app.directive('clickAnywhereButHere', function($document){ return { restrict: 'A', link: function(scope, elem, attr, ctrl) { var elemClickHandler = function(e) { e.stopPropagation(); }; var docClickHandler = function() { scope.$apply(attr.clickAnywhereButHere); }; elem.on('click', elemClickHandler); $document.on('click', docClickHandler); // teardown the event handlers when the scope is destroyed. scope.$on('$destroy', function() { elem.off('click', elemClickHandler); $document.off('click', docClickHandler); }); } } }) 

HTML

 <a click-anywhere-but-here="clickedSomewhereElse()" ng-click="clickedHere()">Don't Click Me!</a> 

目前接受的答案的问题是,如果多次使用该指令,每个具有指令附加的DOM元素将防止冒泡(因此,如果您有两个元素,并且您单击其中任何一个,则两者的callback将是阻止)。

编辑 – 避免jQuery,清理 – 在你的作用域上定义一个函数,并直接传递给这个指令(没有括号),事件将在调用时传递给它。

 app.directive('clickAnywhereButHere', function($document, $parse) { return { restrict: 'A', scope: { callback : '=clickAnywhereButHere' }, link: function(scope, element, attr, ctrl) { var handler = function(event) { if (!element[0].contains(event.target)) { scope.callback(event); } }; $document.on('click', handler); scope.$on('$destroy', function() { $document.off('click', handler); }); } } }); 

HTML中的用法

 <a click-anywhere-but-here="myFunction"></a> 

控制器中的用法

  $scope.myFunction = function (event) { ... } 

注意,你可能需要用scope.$apply()来包装scope.callback(event) scope.$apply()

如果你有很多需要这个指令的元素,这里是另一个性能优化的解决scheme。 (例如一个有100多行的列表,每个都有这个指令)

这将始终只包含一个$文档侦听器

 angular.module('app').directive('clickElsewhere', ['$document', function ($document) { return { link: function postLink(scope, element, attr) { var elsewhere = true; element.on('click', function(e) { elsewhere = false; $document.off('click', clickElsewhere); $document.on('click', clickElsewhere); }); var clickElsewhere = function() { if (elsewhere) { scope.$apply(attr.clickElsewhere); $document.off('click', clickElsewhere); } elsewhere = true; }; } }; }]); 

Max Bates的解决scheme的问题是所有的指令都为$ document.on('click',function(…))添加一个监听器。 导致性能问题的事件。

接受答案的问题已经说明了Max Bates。

在这个博客文章findanwser。

指示:

 app.directive('documentClick', function ($document, $parse) { var linkFunction = function ($scope, $element, $attributes) { var scopeExpression = $attributes.documentClick; var invoker = $parse(scopeExpression); $document.on('click', function (event) { $scope.$apply(function () { invoker($scope, { $event: event }); }); }); }; return linkFunction; }); 

控制器:

 app.controller('PageCtrl', function ($scope) { $scope.click = function (e) { if (!$(e.target).is('.language')) { //do stuff } }; }); 

视图:

 <body ng-controller='PageCtrl' document-click='click($event)'></body> 

这是一个基于Max的解决scheme的变体,但在标准的angular度事件指令方面更自然:

 app.directive('clickOut', function($document) { return { restrict: 'A', scope: { clickOut: '&' }, link: function (scope, element) { var handler = function(event) { if (!element[0].contains(event.target)) { scope.$apply(function () { scope.clickOut({ $event : event }); }); } }; $document.on('click', handler); scope.$on('$destroy', function() { $document.off('click', handler); }); } }; }); 

用法

 <div click-out="myFunction()"></div> 

通过点击事件

 <div click-out="myFunction($event)"></div> 

最简单的方法是检查元素的作用域$ id和clickScope。$ id(click事件的目标作用域$ id)。

 link: function(scope, element, attrs) { //close element when mouse click outside var documentClickHandler = function(event) { var clickScope = angular.element(event.target).scope(); if (clickScope.$id != scope.$id) { //clickScope.$parent is for clicking on the directive children scope if(clickScope.$parent === null || clickScope.$parent.$id != scope.$id){ //Click everywhere but on this element //Do anything you want here, like close your element; } } }; $document.on('click', documentClickHandler); scope.$on('$destroy', function() { $document.off('click', documentClickHandler); }); } 

这个解决scheme的好处:

  1. 你只需要绑定一个$(document) 。 事件的其他发射将取决于$emit范围事件。
  2. 您可以使用expression式click-elsewhere="show=false" ,并click-elsewhere="fn()" ,感谢$parse

码:

 // broadcast click event within AppCtrl app.controller('AppCtrl', function($rootScope) { $(document).on('click', function(e) { // use $emit so the event stays inside $rootScope $rootScope.$emit('click', {target: e.target}); }; }; app.directive('clickElsewhere', function($rootScope) { return { restrict: 'A', compile: function($element, attr) { // store fn in compile so it only execute once var fn = $parse(attr['clickElsewhere']); return function(scope, element) { var offEvent = $rootScope.$on('click', function(event, target) { if ( (element.find($(target)).length) || element.is($(target)) ) return; scope.$apply(function() { fn(scope, {$event: event}); }); }); scope.$on('$destroy', offEvent); }; } }; 

HTML中的用法

  1. click-elsewhere="fn()"
  2. click-elsewhere="show=false"

它可能有点偏离上下文,但是你可以对所有select的项目进行分类(例如“select”),当用户点击“不要点击这里”元素时,可以将当前分类为“select”的所有项目解密,分类具体元素…