javascript - 在元素外部单击时触发事件的指令

标签 javascript angularjs

我知道有很多问题问类似的事情。但是没有人真正解决我的问题。

我正在尝试构建一个指令,当鼠标在当前元素外单击时将执行一个表达式。

为什么我需要这个功能?我正在构建一个应用程序,在这个应用程序中,有 3 个下拉菜单,5 个下拉列表(如选择)。所有这些都是 Angular Directive(指令)。让我们假设所有这些指令都是不同的。所以我们有 8 个指令。并且它们都需要一个相同的功能:当点击元素的外部时,需要隐藏下拉列表。

我有 2 个解决方案,但都有问题:

解决方案 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 的示例:click here

当您点击第一个下拉菜单,然后工作,然后点击第二个输入时,第一个应该隐藏但不会。

解决方案 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 的示例:click here

如果页面中只有一个指令但我的应用程序中没有,则解决方案有效。因为它防止冒泡,所以首先当我点击 dropdown1 时,显示 dropdown1,然后点击 dropdown2,点击事件被阻止,所以即使我在 dropdown1 之外点击,dropdown1 仍然显示在那里。

解决方案 B 在我现在使用的应用程序中运行。但问题是它会导致性能问题。每次单击应用程序中的任何位置时都会处理太多的单击事件。在我目前的例子中,有 8 个点击事件与文档绑定(bind),所以每次点击执行 8 个函数。这导致我的应用程序非常慢,尤其是在 IE8 中。

那么有没有更好的解决办法呢?谢谢

最佳答案

我不会使用 event.stopPropagation(),因为它会导致您在解决方案 A 中看到的那种问题。如果可能,我也会求助于模糊和聚焦事件。当您的下拉列表附加到输入时,您可以在输入失去焦点时将其关闭。

然而,处理文档上的点击事件也不是那么糟糕,所以如果您想避免多次处理同一个点击事件,只需在不再需要时将其与文档解除绑定(bind)即可。除了在下拉列表外单击时要评估的表达式之外,指令还需要知道它是否处于事件状态:

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);
                }
            });
        }
    };
}]);

使用指令时,只需提供另一个表达式,如下所示:

<your-dropdown click-anywhere-but-here="close()" is-active="isDropdownOpen()"></your-dropdown>

我没有测试你的 onClick 函数。我认为它按预期工作。希望这会有所帮助。

关于javascript - 在元素外部单击时触发事件的指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20186438/

相关文章:

angularjs - 当replace=true时如何防止angular指令中的重复属性

javascript - AngularJS/Firebase - 需要登录两次才能成功登录

javascript - Angular 中的 OR 逻辑用于在 $watch() 中返回

javascript - 如何在切换选项卡时更改页面标题并播放通知声音?

Javascript 被转换为汉字

angularjs - Bootstrap 标签类未换行

javascript - 在 JavaScript 中,console.log 为我提供了 for 循环中的每个值。但是 var.text 仅显示最后一个值。为什么是这样?

javascript - 在react js中渲染具有n个级别的目录结构数据

javascript - 如何在 React 中调用 render 之前重置状态?

javascript - 如何在 AngularJS 中保存对象的 JSON 索引位置,而不是对象本身?