在 jQuery click 事件中停止底层 ng-click 的传播
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21485827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Stop propagation of underlying ng-click inside a jQuery click event
提问by maxbeaudoin
A Twitter Bootstrap dropdown
is nested inside a tr
. The tr
is clickable through ng-click
. Clicking anywhere on the page will collapse the dropdown menu. That behavior is defined in a directive through $document.bind('click', closeMenu)
.
Twitter Bootstrapdropdown
嵌套在tr
. 在tr
通过点击ng-click
。单击页面上的任意位置将折叠下拉菜单。该行为在通过 的指令中定义$document.bind('click', closeMenu)
。
So, when menu is opened, and the user click on a row, I want the menu to close (as it does) AND I want to prevent the click event on the row.
因此,当菜单打开并且用户单击一行时,我希望菜单关闭(就像它一样)并且我想阻止该行上的单击事件。
JSFiddle : http://jsfiddle.net/LMc2f/1/
JSFiddle + directive inline: http://jsfiddle.net/9DM8U/1/
JSFiddle :http: //jsfiddle.net/LMc2f/1/
JSFiddle + 内嵌指令:http: //jsfiddle.net/9DM8U/1/
Relevant code from ui-bootstrap-tpls-0.10.0.js :
来自 ui-bootstrap-tpls-0.10.0.js 的相关代码:
angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
var openElement = null,
closeMenu = angular.noop;
return {
restrict: 'CA',
link: function(scope, element, attrs) {
scope.$watch('$location.path', function() { closeMenu(); });
element.parent().bind('click', function() { closeMenu(); });
element.bind('click', function (event) {
var elementWasOpen = (element === openElement);
event.preventDefault();
event.stopPropagation();
if (!!openElement) {
closeMenu();
}
if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
element.parent().addClass('open');
openElement = element;
closeMenu = function (event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
$document.unbind('click', closeMenu);
element.parent().removeClass('open');
closeMenu = angular.noop;
openElement = null;
};
$document.bind('click', closeMenu);
}
});
}
};
}]);
I can't figure out how to stop the underlying ng-click
event inside closeMenu
.
我不知道如何停止ng-click
内部的底层事件closeMenu
。
NOTE : I can't find a way to access $event
so I haven't been able to try $event.stopPropagation()
.
注意:我找不到访问的方法,$event
所以我无法尝试$event.stopPropagation()
。
采纳答案by Stewie
The dropdown directive binds the click event on the document, but when you click on the row, the event starts propagating from the target element downto the root document node (td
-> tr
-> table
-> document
).
dropdown 指令将 click 事件绑定到文档上,但是当您单击该行时,该事件开始从目标元素向下传播到根文档节点 ( td
-> tr
-> table
-> document
)。
So that's why your ng-click
handler, that you have on your row, always gets called, even though the directive is "stopping" the bubble on document click.
所以这就是为什么你的ng-click
处理程序,你在你的行上,总是被调用,即使指令在文档点击时“停止”气泡。
Solution is to use the useCaptureflag when adding the click handler for the document.
解决方案是在为文档添加单击处理程序时使用useCapture标志。
After initiating capture, all events of the specified type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. mdn
启动捕获后,指定类型的所有事件将被分派到注册的侦听器,然后再分派到 DOM 树中它下面的任何 EventTarget。域名
Now, to instruct the dropdown directive to use your own handler, you need to change the source of the directive. But it's a third party directive, and you probably don't want to do that, for maintability reasons.
现在,要指示下拉指令使用您自己的处理程序,您需要更改指令的来源。但这是第三方指令,出于可维护性的原因,您可能不想这样做。
This is where the powerful angular $decorator kicks in. You may use the $decorator to change the source of the third-party module on-the-fly, without actually touching the actual source files.
这就是强大的 angular $decorator 发挥作用的地方。您可以使用 $decorator 即时更改第三方模块的源代码,而无需实际接触实际的源文件。
So, with decorator in place, and with custom event handler on the document node, this is how you can make that dropdown behave:
因此,使用装饰器并在文档节点上使用自定义事件处理程序,您可以通过以下方式使下拉菜单行为:
var myApp = angular.module('myApp', []);
/**
* Original dropdownToggle directive from ui-bootstrap.
* Nothing changed here.
*/
myApp.directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
var openElement = null,
closeMenu = angular.noop;
return {
restrict: 'CA',
link: function(scope, element, attrs) {
scope.$watch('$location.path', function() { closeMenu(); });
element.parent().bind('click', function() { closeMenu(); });
element.bind('click', function (event) {
var elementWasOpen = (element === openElement);
event.preventDefault();
event.stopPropagation();
if (!!openElement) {
closeMenu();
}
if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
element.parent().addClass('open');
openElement = element;
closeMenu = function (event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
$document.unbind('click', closeMenu);
element.parent().removeClass('open');
closeMenu = angular.noop;
openElement = null;
};
$document.bind('click', closeMenu); /* <--- CAUSE OF ALL PROBLEMS ----- */
}
});
}
};
}]);
/**
* This is were we decorate the dropdownToggle directive
* in order to change the way the document click handler works
*/
myApp.config(function($provide){
'use strict';
$provide.decorator('dropdownToggleDirective', [
'$delegate',
'$document',
function ($delegate, $document) {
var directive = $delegate[0];
var openElement = null;
var closeMenu = angular.noop;
function handler(e){
var elm = angular.element(e.target);
if(!elm.parents('.dropdown-menu').length){
e.stopPropagation();
e.preventDefault();
}
closeMenu();
// After closing the menu, we remove the all-seeing handler
// to allow the application click events to work nnormally
$document[0].removeEventListener('click', handler, true);
}
directive.compile = function(){
return function(scope, element) {
scope.$watch('$location.path', closeMenu);
element.parent().bind('click', closeMenu);
element.bind('click', function (event) {
var elementWasOpen = (element === openElement);
event.preventDefault();
event.stopPropagation();
if (!!openElement) {
closeMenu();
}
if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
element.parent().addClass('open');
openElement = element;
closeMenu = function (event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
$document.unbind('click', closeMenu);
element.parent().removeClass('open');
closeMenu = angular.noop;
openElement = null;
};
// We attach the click handler by specifying the third "useCapture" parameter as true
$document[0].addEventListener('click', handler, true);
}
});
};
};
return $delegate;
}
]);
});
UPDATE:
更新:
Note that the updated custom handler will prevent bubbling unless the target element is the actual drop-down option. This will solve the problem where click event was being prevented even when clicking on the drop-down options.
请注意,除非目标元素是实际的下拉选项,否则更新的自定义处理程序将防止冒泡。这将解决即使单击下拉选项也阻止单击事件的问题。
This still won't prevent the event to bubble down to row (from drop-down option), but this is something that's not in any way related to the drop-down directive. Anyway, to prevent such bubbling you can pass the $event
object to ng-click
expression function and use that object to stop the even to bubble down to the table row:
这仍然不会阻止事件冒泡到行(从下拉选项),但这与下拉指令没有任何关系。无论如何,为了防止这种冒泡,您可以将$event
对象传递给ng-click
表达式函数并使用该对象来阻止偶数冒泡到表格行:
<div ng-controller="DropdownCtrl">
<table>
<tr ng-click="clicked('row')">
<td>
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle">
Action <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="choice in items">
<a ng-click="clicked('link element', $event)">{{choice}}</a>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
function DropdownCtrl($scope) {
$scope.items = [
"Action",
"Another action",
"Something else here"
];
$scope.clicked = function(what, event) {
alert(what + ' clicked');
if(event){
event.stopPropagation();
event.preventDefault();
}
}
}
回答by user2479438
I would lean towards just calling $event.stopPropagation()
from within the template itself. Logic related to events most likely belongs there. Should make unit testing easier as well. Someone looking at the template would also know that the event didn't bubble up without looking at the underlying controller.
我倾向于只$event.stopPropagation()
从模板内部调用。与事件相关的逻辑很可能属于那里。也应该使单元测试更容易。查看模板的人也会知道,如果不查看底层控制器,事件不会冒泡。
<div ng-click="parentHandler()">
<div ng-click="childHandler(); $event.stopPropagation()"></div>
</div>
回答by koolunix
you need to pass the event of ng-click on line 2 of your fiddle, and then do preventDefault and stopPropigation on that object within your method do
您需要在小提琴的第 2 行传递 ng-click 事件,然后在您的方法中对该对象执行 preventDefault 和 stopPropigation
<tr ng-click="do($event)">