取消嵌套ng-click调用之间的事件传播的最佳方法是什么?


180

这是一个例子。假设我想像许多网站一样覆盖图片。因此,当您单击缩略图时,整个窗口上方会出现黑色叠加层,并且较大的图像版本位于其中。单击黑色覆盖将其关闭;单击该图像将调用显示下一张图像的函数。

的HTML:

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
    <img src="http://some_src" ng-click="nextImage()"/>
</div>

JavaScript:

function OverlayCtrl($scope) {
    $scope.hideOverlay = function() {
        // Some code to hdie the overlay
    }
    $scope.nextImage = function() {
        // Some code to find and display the next image
    }
}

问题是,这种设置,如果你点击图片,都nextImage()hideOverlay()被调用。但是我想要的只是nextImage()被称为。

我知道您可以使用以下nextImage()功能捕获和取消事件:

if (window.event) {
    window.event.stopPropagation();
}

...但是我想知道是否有更好的AngularJS方法,不需要我用此代码段在叠加层内的元素上添加所有函数的前缀。


1
return false在功能结束时
Jonathan de M.

1
我相信这是这样做的方法onclick,而不是ng-click
Michael Scheper

Answers:


193

@JosephSilber所说的,或者将$ event对象传递给ng-click回调并停止其内部的传播:

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
  <img src="http://some_src" ng-click="nextImage($event)"/>
</div>
$scope.nextImage = function($event) {
  $event.stopPropagation();
  // Some code to find and display the next image
}

8
使用$ event.preventDefault(); v> 1.5
KoolKabin

3
@KoolKabin我正在使用Angular 1.5.8和$ event.stopPropagation()仍然可以正常工作。$ event.preventDefault()但是不起作用
HammerNL '16

1
很奇怪,因为我有相反的情况。stopPropagation不再起作用,但preventDefault()起作用。唯一的不同是,我直接在ng-click上致电。<tr ng-click =“ ctr.foo(); $ event.preventDefault()”> ..... </ tr>
MilitelloVinx

171

用途$event.stopPropagation()

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
    <img src="http://some_src" ng-click="nextImage(); $event.stopPropagation()" />
</div>

这是一个演示:http : //plnkr.co/edit/3Pp3NFbGxy30srl8OBmQ?p=preview


我进入ReferenceError: $event is not defined控制台。
cilphex

是的,它确实...当我从头开始编写一个单独的简单版本时,它也可以工作。我试图找出是什么使它无法在我的开发代码中工作。
Dunno

@cilphex-您使用的是Angular的最新版本吗?
约瑟夫·西尔伯

是的-刚发现问题。测试时,我尝试将$event.stopPropagation()其放置在不起作用的地方。忘记删除它。因此,即使我把它放在可以正常工作的地方,也被第二次叫错了。消除了功能失调的重复项,它是成功的。谢谢你的帮助。
cilphex

101

我喜欢为此使用指令的想法:

.directive('stopEvent', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            element.bind('click', function (e) {
                e.stopPropagation();
            });
        }
    };
 });

然后使用如下指令:

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
    <img src="http://some_src" ng-click="nextImage()" stop-event/>
</div>

如果需要,可以使该解决方案更通用,例如对另一个问题的答案:https : //stackoverflow.com/a/14547223/347216


2
如果您的元素是动态添加到DOM中/从DOM中删除的,请不要忘记监视元素中的“ $ destroy”事件,以便可以取消绑定处理程序。例如,element.on('$ destroy',function(){/ *在这里进行解除绑定* /});
broc.seib 2014年

stop-eventhtml属性?我在Mozilla开发人员网络或其他任何地方都找不到它。
Ehtesh Choudhury 2014年

3
@EhteshChoudhury-“停止事件”是我在上面的JS代码段中创建的新指令。在此处查看有关声明和使用指令的更多信息:docs.angularjs.org/guide/directive
David Ipsen

不错的解决方案!我实际上angular-eat-event在凉亭上注册了指令,它的工作方式类似!
gion_13 2015年

它似乎不起作用,ng-click在指令之前求值。因此我可以防止执行指令,但不能反过来执行。
拉斐尔

31

有时,这样做可能最有意义:

<widget ng-click="myClickHandler(); $event.stopPropagation()"/>

我选择这样做是因为我不想myClickHandler()在事件使用的许多其他地方停止事件传播。

当然,我可以在处理函数中添加一个布尔参数,但stopPropagation()它比just有意义得多true


2
我一直喜欢这个版本,似乎元素的嵌套不应该与我正在调用的函数有关
mcfedr

我必须添加$event.stopPropagation()内部元素。
基什·帕瓦尔

@KishorPawar:为什么?我在答案中所做的方式对您不起作用吗?如果不是,那么对我们来说找出这是由于Angular工作方式的变化还是代码中的问题是一件好事。
Michael Scheper '02

checkbox包裹了@MichaelScheper div。我div正在读12列,checkbox说3列。我想A在单击时调用一个函数,divB在单击时调用一个函数checkbox。因此,当我单击checkbox两个函数时,它们都会被调用。当添加ng-click="$event.stopPropagation()"到时checkbox,仅B调用了函数。
Kishor Pawar

@KishorPawar:啊。是的,我希望如此,并且的确stopPropagation()是要停止将事件传播到父元素。我不确定您的呼叫方式,B因为未指定ng-click,但答案的关键是您可以执行以下操作:<checkbox ng-click="B(); $event.stopPropagation()">。您不必在中包含该stopPropagation()函数B
Michael Scheper

7

这对我有用:

<a href="" ng-click="doSomething($event)">Action</a>

this.doSomething = function($event) {
  $event.stopPropagation();
  $event.preventDefault();
};

我想通过按下或不按下Ctrl键来处理鼠标单击。当用户按住Ctrl键单击各种项目时,要停止选择(并突出显示)浏览器中的HTML元素(本例中为IE 11),我必须使用ng-mousedown事件并通过$ event取消默认行为.preventDefault()
pholpar

4

如果您不想将停止传播添加到所有链接,则也可以使用。更具可扩展性。

$scope.hideOverlay( $event ){

   // only hide the overlay if we click on the actual div
   if( $event.target.className.indexOf('overlay') ) 
      // hide overlay logic

}

2
此解决方案适用于所提供的示例(很棒!),但是当外部div在href / ng-click之前具有n个或多个嵌套元素时,该解决方案将不起作用。然后,当调用hideOverlay()回调时,目标是嵌套元素之一,而不是具有ng-click指令的最外面的元素。要想处理嵌套元素,可以使用jquery来获取父元素,并查看第一个可单击的(a,ng-click等)父元素是否具有overlay类,但这看起来有点麻烦……
Amir

console.log(“ potatooverlay” .indexOf(“ overlay”)!= -1); console.log(/ \ boverlay \ b / g.test(“ potatooverlay”));

角可以让你做到event.target.classList.indexOf('overlay') 这把戏工作正常甚至当DIV嵌套在其他的div
DELTREE

0

如果在模板的父元素上插入ng-click =“ $ event.stopPropagation”,则会在树上冒起stopPropogation时被捕获,因此您只需为整个模板编写一次即可。


0

您可以在ng-click其上注册另一个指令,以修改的默认行为ng-click并停止事件传播。这样,您无需$event.stopPropagation手动添加。

app.directive('ngClick', function() {
    return {
        restrict: 'A',
        compile: function($element, attr) {
            return function(scope, element, attr) {
                element.on('click', function(event) {
                    event.stopPropagation();
                });
            };
        }
    }
});

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.