如何在Angular中有效地检测元素点击?

时间:2016-03-25 20:50:41

标签: javascript angularjs

这是我的指令

的链接功能
    function linkFunc(scope, element, attr){

        // Detect Element Click Logic
        scope.myCtrl.clickedElsewhere = {value: true};

        $document.on('click', function(){
            scope.myCtrl.clickedElsewhere.value = true;
            scope.$apply();
        });

        element.on('click', function(){
            event.stopPropagation();
            scope.myCtrl.clickedElsewhere.value = false;
            scope.$apply();
        });
        // End Detect Element Click Logic

    }

正如我们所看到的,我们使用了$document.on()scope.apply,这意味着对于文档中任何位置的每次点击,我们都会触发摘要周期。如果我们有很多$watch个触发器,这可能会导致网页速度变慢。虽然这种实现并不是很有效,但我无法想到其他方法来检测元素点击和关闭元素点击以扩展和收缩我的元素。

有人可以提供一些见解吗?

由于

3 个答案:

答案 0 :(得分:3)

在文档点击处理程序中调用scope.myCtrl.clickedElsewhere.value之前检查$apply()会更有效率:

$document.on('click', function(){
   if(!scope.myCtrl.clickedElsewhere.value){
       scope.myCtrl.clickedElsewhere.value = true;
       scope.$apply();
   }
});

当它的状态没有变化时,这将防止不必要的摘要。

您还可以删除此单击侦听器,并在单击元素时重新应用它:

function docHandler() {
  if (!scope.myCtrl.clickedElsewhere.value) {
    scope.myCtrl.clickedElsewhere.value = true;
    scope.$apply();
    $document.off('click');//remove event listener completely
  }
}

element.on('click', function() {
  event.stopPropagation();
  scope.myCtrl.clickedElsewhere.value = false;
  scope.$apply();
  $document.on('click', docHandler);// add document listener
});

答案 1 :(得分:2)

您可以创建一个关闭指令

myApp.directive('clickOff', function($parse, $document) {
  var dir = {
    compile: function($element, attr) {
      // Parse the expression to be executed
      // whenever someone clicks _off_ this element.
      var fn = $parse(attr["clickOff"]);
      return function(scope, element, attr) {
        // add a click handler to the element that
        // stops the event propagation.
        element.bind("click", function(event) {
          event.stopPropagation();
        });
        angular.element($document[0].body).bind("click",function(event) {
            scope.$apply(function() {
                fn(scope, {$event:event});
            });
        });
      };
    }
  };
  return dir;
});

用法:

<div ng-app="myApp">
  <button ng-click="show=true;" click-off="show=false;">
      Click Me
  </button>
  <div class="message" ng-show="show" ng-init="show = false">
    You clicked on the button. Now click anywhere else...
  </div>
</div>

实施例: https://jsfiddle.net/oytdwyxj/

答案 2 :(得分:0)

你可以创建两个指令,它们之间有父/子关系。

看看我创建的手风琴指令,我相信这是你需要的,以便弄清楚你的情况。

指令:

app.directive("accordion", function () {
    return {
        template: "<div ng-transclude></div>",
        restrict: "E",
        scope: {
            closeall: "@"
        },
        transclude: true,
        replace: true,
        controller: function ($scope, $element, $attrs) {
            var itensScope = [];

            var addItemScope = function (scope) {
                itensScope.push(scope);
            };

            var closeItens = function () {
                if ($scope.closeall == "false") return;
                angular.forEach(itensScope, function (scope) {
                    scope.showItem = false;
                });
            }
            return {
                addItemScope: addItemScope,
                closeItens: closeItens
            };
        }
    };
});
app.directive("accordionItem", function () {
    return {
        template: "<div><div class='item' ng-class='{itemClose: !showItem}'>{{title}}</div><div class='itemInformation' ng-show='showItem' ng-transclude></div></div>",
        restrict: "E",
        transclude: true,
        replace: true,
        scope: {
            title: "@"
        },
        require: "^accordion",
        link: function (scope, element, attrs, ctrl, transcludeFn) {
            ctrl.addItemScope(scope);
            element.bind("click", function () {
                ctrl.closeItens();
                scope.$apply(function () { 
                    scope.showItem = !scope.showItem; 
                });
            });
        }
    };
});

用法:

<accordion closeall="true">
        <accordion-item title="A">
            <p>
                A
            </p>
        </accordion-item>
        <accordion-item title="B">
            <p>
                B
            </p>
        </accordion-item>
</accordion>

我刚刚创建了这个示例,它可以在我的GitHub中找到:https://github.com/rodrigobranas/branas-angular-ui