所以当菜单被打开并且用户点击一行时,我想要关闭菜单(就像这样),我想阻止该行上的点击事件。
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()。
解决方法
所以这就是为什么你的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(); } } }