所以当菜单被打开并且用户点击一行时,我想要关闭菜单(就像这样),我想阻止该行上的点击事件。
JSFiddle:http://jsfiddle.net/LMc2f/1/
JSFiddle指令内联:http://jsfiddle.net/9DM8U/1/
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); } }); } }; }]);
我不知道如何在closeMenu中停止底层的ng-click事件。
注意:我找不到访问$ event的方法,所以我无法尝试$ event.stopPropagation()。
解决方法@H_502_17@
下拉式指令绑定文档上的点击事件,但是当您单击行时,事件将从目标元素向下传播到根文档节点(td – > tr – > table – >文档)。
所以这就是为什么你的ng点击处理程序,你的行,总是被调用,即使指令是“停止”文档点击的气泡。
解决方案是在添加文档的点击处理程序时使用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. 07000
现在,要指示下拉式指令使用您自己的处理程序,您需要更改指令的来源。但是这是第三方的指令,你可能不想这样做,因为可维护性的原因。
这是强大的角度$装饰器踢的地方。您可以使用$ decorator来即时更改第三方模块的来源,而不会实际触及实际的源文件。
因此,使用装饰器,并在文档节点上使用自定义事件处理程序,这是如何使该下拉行为:
var myApp = angular.module('myApp',[]);
/**
* Original dropdownToggle directive from ui-bootstrap.
* Nothing changed here.
*/
myApp.directive('dropdownToggle',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',true);
}
});
};
};
return $delegate;
}
]);
});
更新:
请注意,更新的自定义处理程序将防止冒泡,除非目标元素是实际的下拉选项。这将解决即使点击下拉选项也可以防止点击事件的问题。
这仍然不会阻止事件逐渐下降到行(从下拉列表中选择),但这与下拉式指令无关。无论如何,为了防止这种冒泡,您可以将$ 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();
}
}
}
所以这就是为什么你的ng点击处理程序,你的行,总是被调用,即使指令是“停止”文档点击的气泡。
解决方案是在添加文档的点击处理程序时使用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. 07000
现在,要指示下拉式指令使用您自己的处理程序,您需要更改指令的来源。但是这是第三方的指令,你可能不想这样做,因为可维护性的原因。
这是强大的角度$装饰器踢的地方。您可以使用$ decorator来即时更改第三方模块的来源,而不会实际触及实际的源文件。
因此,使用装饰器,并在文档节点上使用自定义事件处理程序,这是如何使该下拉行为:
var myApp = angular.module('myApp',[]); /** * Original dropdownToggle directive from ui-bootstrap. * Nothing changed here. */ myApp.directive('dropdownToggle',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',true); } }); }; }; return $delegate; } ]); });
更新:
请注意,更新的自定义处理程序将防止冒泡,除非目标元素是实际的下拉选项。这将解决即使点击下拉选项也可以防止点击事件的问题。
这仍然不会阻止事件逐渐下降到行(从下拉列表中选择),但这与下拉式指令无关。无论如何,为了防止这种冒泡,您可以将$ 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(); } } }