点击任何地方但这里事件

IT技术 javascript angularjs
2021-02-06 08:41:12

想知道我将如何实现“除此元素之外的任何地方都点击”事件。

我有一些东西可以与文件资源管理器中的文件列表进行比较。您可以选择某些元素,但如果您在元素控制器外部单击,则需要取消选择所有内容。

在此处输入图片说明

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

更新

澄清一下,我不是在问如何用 jQuery 做到这一点。

6个回答

编辑:这个古老的答案中有几个问题。

*另外:标记社区维基(我没有积分)因为错误

  1. N 调用该指令的 N 次使用。对于具有匹配表达式的同一范围内的使用,这可能是不可取的。

  2. 没有任何东西会破坏事件处理程序!!!!坏的!坏的!坏的!

所以,我正在更新这个答案。希望它没有给任何人带来太多麻烦。

更新答案

这是一个修复了这些问题的新 plunker ……个别应用程序开发人员可能会遇到其他问题。这只是如何处理此问题的示例。

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

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

你已经找到了你找到的一个答案,但我已经为你整理了一个 plunk来告诉你它缺少什么。

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>
请@mlunoe 你能举例说明你的观点吗?我不知道如何检测何时show发生。谢谢!
2021-03-18 08:41:12
仅供参考, $eval 和 $apply 可以合并为一行: scope.$apply(attr.clickAnywhereButHere);
2021-03-22 08:41:12
是的,对不起。那不是很具体。我使用带有信息的引导程序弹出窗口实现了此功能。单击弹出窗口以外的任何其他地方时,我需要关闭弹出窗口。我修改了 plunker 以满足我的需要:plnkr.co/edit/ioVy1u?p=preview
2021-03-25 08:41:12
非常真实。(在我的辩护中,我在几个月前回答了这个问题。:P)我会进行编辑。
2021-03-30 08:41:12
如果您想像我一样使用此指令创建“在单击外部时隐藏”功能,请记住bind将 click 功能设置为$documenton showundbind然后再将其设置为 on hide
2021-04-01 08:41:12

当前接受的答案的问题是,如果您多次使用该指令,则附加了该指令的每个 DOM 元素都将防止冒泡(因此,如果您将它放在两个元素上,并且单击其中一个,则两者的回调都将是阻止)。

编辑 - 避免使用 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.callback(event)scope.$apply()

正如解决方案所述,您可以不带括号地传入函数,它会起作用(传递了对函数的引用)。传递为 '&' 要求您对参数进行硬编码,我不喜欢这样做,即使它会强制一致性。
2021-03-15 08:41:12
您需要将回调:'=clickAnywhereButHere' 更改为回调:'&clickAnywhereButHere'。这是一个函数。您可能还想在 $apply 中包装 scope.callback(event)。
2021-03-16 08:41:12
另请注意,如果您不想要隔离范围,您可以轻松删除范围声明并在处理程序内使用类似 blesh 答案中的语法: scope.$apply(attr.clickAnywhereButHere)
2021-03-17 08:41:12
为了让它更短一点, $document.click(handler)
2021-04-09 08:41:12
除了element.has(event.target).length === 0您可以简单地编写element[0].contains(evt.target)(不需要 jQuery),请参阅developer.mozilla.org/en-US/docs/Web/API/Node.contains您也可以$.contains(element[0], evt.target)使用 jQuery编写它
2021-04-13 08:41:12

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

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

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 解决方案的问题是所有指令都为 $document.on('click', function(...)); 添加了一个侦听器。导致性能问题的事件。

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

工作精美。也简单多了!
2021-04-03 08:41:12

在这篇文上找到了答案

指示:

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>
这很接近,但是如果您单击文档内的元素,则单击事件将冒泡并触发您的全局事件。我不确定 ng-click 是否停止传播。
2021-03-14 08:41:12
很酷,一个使用 $parse 然后在 $parse 返回的函数中指定“locals”参数的示例。(很难找到这样的例子。)
2021-04-04 08:41:12

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

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>