当点击元素外部时触发事件的指令。
当点击元素外部时触发事件的指令。
我知道有很多类似的问题。但是没有人真正解决我的问题。
我正在尝试构建一个指令,该指令将在鼠标点击当前元素之外时执行表达式。
为什么我需要这个功能?我正在构建一个应用程序,在这个应用程序中,有3个下拉菜单,5个下拉列表(类似于chosen)。所有这些都是angular指令。假设这些指令都是不同的。所以我们有8个指令。并且所有它们都需要一个相同的功能:当单击元素之外时,需要隐藏下拉菜单。
对于此,我有两个解决方案,但都有问题:
方法A:
app.directive('clickAnywhereButHere', function($document){ return { restrict: 'A', link: function(scope, elem, attr, ctrl) { elem.bind('click', function(e) { // this part keeps it from firing the click on the document. e.stopPropagation(); }); $document.bind('click', function() { // magic here. scope.$apply(attr.clickAnywhereButHere); }) } } })
这里是方法A的示例:点击此处
当您单击第一个下拉菜单时,然后按工作,然后单击第二个输入框,第一个应该隐藏但没有隐藏。
方法B:
app.directive('clickAnywhereButHere', ['$document', function ($document) { directiveDefinitionObject = { link: { pre: function (scope, element, attrs, controller) { }, post: function (scope, element, attrs, controller) { onClick = function (event) { var isChild = element.has(event.target).length > 0; var isSelf = element[0] == event.target; var isInside = isChild || isSelf; if (!isInside) { scope.$apply(attrs.clickAnywhereButHere) } } $document.click(onClick) } } } return directiveDefinitionObject }]);
这里是方法B的示例:点击此处
如果页面中只有一个指令,则方法A有效,但在我的应用程序中无效。因为它会阻止冒泡,所以首先当我单击dropdown1时,显示dropdown1,然后单击dropdown2,在单击事件被阻止的情况下,dropdown1仍然在那里显示,即使我单击了dropdown1的外部。
方法B在我现在使用的应用程序中有效。但问题是它会导致性能问题。每次单击应用程序中的任何位置时,都会处理太多的单击事件。在我的当前情况下,有8个单击事件绑定到文档中,因此每次单击都会执行8个函数。这使我的应用程序非常缓慢,特别是在IE8中。
那么有没有更好的解决方案?谢谢
你应该使用ngBlur和ngFocus来显示或隐藏你的下拉框。当有人点击它时,它会获得焦点,否则它就会变模糊。
此外,请参考这个问题如何在AngularJS中设置输入框的焦点?。
编辑:
对于每个指令(下拉菜单或列表,我们称之为Y),当你点击一个元素(我们称之为X)时,你需要显示它,并且在点击Y以外的任何地方(不包括X)时隐藏它。
Y有一个属性isYvisisble。
因此,当有人点击X(ng-click)时,将"isYvisible"设置为真,并将焦点设置在Y上。
当有人点击Y以外的地方(ng-blur)时,你将"isYvisible"设置为假,它就会被隐藏起来。
你需要在两个不同的元素/指令之间分享一个变量("isYvisible"),你可以使用控制器或服务的作用域来实现这一点。还有其他的替代方法,但这超出了本问题的范围。
我不会使用event.stopPropagation(),因为它会导致解决方案A中出现的问题。如果可能的话,我也会考虑模糊和聚焦事件。当您的下拉菜单附加在输入框上时,您可以在输入框失去焦点时关闭它。
然而,处理文档上的点击事件也不错,如果您想避免多次处理相同的单击事件,只需在它不再需要时从文档中取消绑定。除了点击下拉列表外,该指令还需要知道它是否启用:
app.directive('clickAnywhereButHere', ['$document', function ($document) { return { link: function postLink(scope, element, attrs) { var onClick = function (event) { var isChild = $(element).has(event.target).length > 0; var isSelf = element[0] == event.target; var isInside = isChild || isSelf; if (!isInside) { scope.$apply(attrs.clickAnywhereButHere) } } scope.$watch(attrs.isActive, function(newValue, oldValue) { if (newValue !== oldValue && newValue == true) { $document.bind('click', onClick); } else if (newValue !== oldValue && newValue == false) { $document.unbind('click', onClick); } }); } }; }]);
使用指令时,只需提供另一个表达式,如下所示:
我没有测试过您的onClick函数。我希望这有所帮助。