点击此处所有活动

时间:2012-10-17 09:37:41

标签: javascript angularjs

想知道如何实现“点击无处不在但在此元素上”活动。

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

enter image description here

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

更新

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

8 个答案:

答案 0 :(得分:68)

编辑:这个陈旧的答案中有几个问题。

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

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

  2. 没有什么可以扯掉事件处理器!!!!坏!坏!坏!

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

    更新回答

    Here's a new plunker with those issues fixed ...个别应用程序开发人员可能遇到其他问题。这只是如何处理这个问题的一个例子。

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

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

    您已经找到了一个答案,但我已经 put together a 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>
    

答案 1 :(得分:28)

当前接受的答案存在的问题是,如果你多次使用该指令,那么附加指令的每个DOM元素都会阻止冒泡(所以如果你有两个元素,并且你点击其中任何一个,那么回调就是两者都将被封锁。)

编辑 - 避免使用jQuery,清理 - 在作用域上定义一个函数,并将其直接传递给该指令(不带括号),并在调用时将事件传递给它。 / p>

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()

答案 2 :(得分:7)

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

这将始终只包含一个$ document listener

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添加一个监听器(&#39; click&#39;,function(...));出现性能问题的事件。

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

答案 3 :(得分:3)

blog post找到答案。

指令:

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>

答案 4 :(得分:2)

这是基于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>

答案 5 :(得分:1)

最简单的方法是检查元素的范围。$ 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);
    });
}

答案 6 :(得分:0)

此解决方案的好处:

  1. 您只需要绑定到$(document)。其他事件触发将取决于范围事件的$emit
  2. 由于$parse,您可以同时使用click-elsewhere="show=false"click-elsewhere="fn()"两种表达方式。
  3. 代码:

    // 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"

答案 7 :(得分:-1)

它可能有点脱离上下文,但是当用户点击“不要点击此处”元素时,您可以对所有选定项目进行分类(例如“选中”),您可以解密当前分类为“已选中”的所有项目“,并对特定元素进行分类......