angularjs - Stop propagation of underlying ng-click inside a jQuery click event -


a twitter bootstrap dropdown nested inside tr. tr clickable through ng-click. clicking anywhere on page collapse dropdown menu. behavior defined in directive through $document.bind('click', closemenu).

so, when menu opened, , user click on row, want menu close (as does) , want prevent click event on row.

jsfiddle : http://jsfiddle.net/lmc2f/1/
jsfiddle + directive inline : http://jsfiddle.net/9dm8u/1/

relevant code 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 stop underlying ng-click event inside closemenu.

note : can't find way access $event haven't been able try $event.stoppropagation().

the dropdown directive binds click event on document, when click on row, event starts propagating target element down root document node (td -> tr -> table -> document).

so that's why ng-click handler, have on row, gets called, though directive "stopping" bubble on document click.

solution use usecapture flag when adding click handler document.

after initiating capture, events of specified type dispatched registered listener before being dispatched eventtarget beneath in dom tree. mdn

now, instruct dropdown directive use own handler, need change source of directive. it's third party directive, , don't want that, maintability reasons.

this powerful angular $decorator kicks in. may use $decorator change source of third-party module on-the-fly, without touching actual source files.

so, decorator in place, , custom event handler on document node, how can make dropdown behave:

fiddle

var myapp = angular.module('myapp', []);  /**  * original dropdowntoggle directive 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 problems ----- */         }       });     }   }; }]);   /**  * decorate dropdowntoggle directive  * in order change way 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 menu, remove all-seeing handler           // allow application click events 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;                 };                   // attach click handler specifying third "usecapture" parameter true                 $document[0].addeventlistener('click', handler, true);               }             });           };         };          return $delegate;       }   ]);  }); 

update:

note updated custom handler prevent bubbling unless target element actual drop-down option. solve problem click event being prevented when clicking on drop-down options.

this still won't prevent event bubble down row (from drop-down option), that's not in way related drop-down directive. anyway, prevent such bubbling can pass $event object ng-click expression function , use object stop bubble down table row:

<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();     }   }  } 

Comments

Popular posts from this blog

html - Sizing a high-res image (~8MB) to display entirely in a small div (circular, diameter 100px) -

java - IntelliJ - No such instance method -

identifier - Is it possible for an html5 document to have two ids? -