76

I am trying to "stopPropagation" to prevent a Twitter Bootstrap navbar dropdown from closing when an element (link) inside an li is clicked. Using this method seems to be the common solution.

In Angular, seems like a directive is the place to do this? So I have:

// do not close dropdown on click
directives.directive('stopPropagation', function () {
    return {
        link:function (elm) {            
            $(elm).click(function (event) {                
                event.stopPropagation();
            });
        }
    };
});

... but the method does not belong to element:

TypeError: Object [object Object] has no method 'stopPropagation'

I tie in the directive with

<li ng-repeat="foo in bar">
  <div>
    {{foo.text}}<a stop-propagation ng-click="doThing($index)">clickme</a>
  </div>
</li>

Any suggestions?

Community
  • 1
  • 1
Robert Christian
  • 18,218
  • 20
  • 74
  • 89
  • You should make some room in the tags for [tag:javascript] (or even [tag:jquery]). – Joseph Silber Jan 27 '13 at 05:38
  • Good suggestion. Made the edit. – Robert Christian Jan 27 '13 at 05:39
  • The reason the `event.stopPropagation()` doesn't work in your code is that AngularJS and jQuery have *two separate event cycles*. That's one reason why it's generally a bad idea to use them both. Your click event defined with `ngClick` uses Angular, but you're trying to use jQuery to stop the event propagation. – Josh David Miller Jan 27 '13 at 07:04

4 Answers4

156

I've used this way: Created a directive:

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

that could be used this way:

<a ng-click='expression' stop-event='click'>

This is more generic way of stopping propagation of any kind of events.

Danny Bullis
  • 3,043
  • 2
  • 29
  • 35
Valentyn Shybanov
  • 19,331
  • 7
  • 66
  • 59
  • BTW, can you elaborate on the restrict keyword? Not finding a good resource for what that means exactly. – Robert Christian Jan 28 '13 at 04:26
  • Sure! http://docs.angularjs.org/guide/directive "Directive Definition Object": restrict - String of subset of EACM which restricts the directive to a specific directive declaration style. If omitted directives are allowed on attributes only. – Valentyn Shybanov Jan 28 '13 at 07:10
  • This is an interesting approach... I like it. Thanks! – incutonez Apr 30 '13 at 17:41
  • 1
    Same solution for .prefentDefault() in IE7 to keep page reloads from happening. Great Solution! – Malkus Jun 03 '13 at 18:13
  • 1
    Oh lord i love directives – Deminetix Nov 23 '13 at 04:04
  • This does not work for me in Safari Mobile iOS 7. But the $event.stopProgation(); works. – mlegenhausen Dec 05 '13 at 14:39
  • var to = attr.ngStopEvent != "*" ? attr.ngStopEvent : 'blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error'; // to support wildcard in element.bind(to, – Leblanc Meneses Nov 17 '14 at 01:59
  • Yo, I added an `if(attr && attr.stopEvent)` check just to make things a little more safe. Let me know if this isn't cool. Hope it helps :]. Thanks for the great snippet – Danny Bullis Jun 03 '15 at 22:39
  • I used this solution; it works fine and it seems more "clean" for me than the selected answer. – Christian Martinez Aug 12 '15 at 15:38
  • Thank you so much! You save my life! :) I used it to scroll content inside of angular material design bottomSheet : https://material.angularjs.org/latest/demo/bottomSheet Here is how i used it:
    – Renan Franca Oct 27 '15 at 01:41
  • I have a checkbox and a select inside the heading of an accordion. When pressing on the checkbox or the select, the accordion opened or closed (not the desired effect). I solved this by adding `ng-click="$event.stopImmediatePropagation();"` to the checkbox and `ng-click="$event.stopImmediatePropagation();$event.preventDefault();"` to the select, and now everything Works as expected. – aplon Feb 19 '16 at 02:03
123

"Currently some directives (i.e. ng:click) stops event propagation. This prevents interoperability with other frameworks that rely on capturing such events." - link

... and was able to fix without a directive, and simply doing:

<a ng-click="doThing($index); $event.stopPropagation();">x</a>
Robert Christian
  • 18,218
  • 20
  • 74
  • 89
  • 4
    This was perfect. I had a dropdown with a form in it. I added ng-click="$event.stopPropagation()" to the div around the form and that fixed the dropdown from going away when the form inputs were clicked. Thanks! – thaspius Aug 06 '13 at 01:02
  • simple solution. worked perfectly. I would prefer this rather than creating a directive. – Nithin Emmanuel Jan 23 '14 at 05:01
  • Indeed, simplier than having to create a directive. works fine for me. Thanks – estellechvl Sep 08 '14 at 12:06
  • 3
    Simple for a single-use scenario. I'd recommend using a directive to anyone who needs to use this code in multiple places throughout the view - for one, it's easier to extend the functionality, handle more events, and especially to try to keep logic out of the markup as much as possible. Cheers! – Danny Bullis Jun 03 '15 at 22:55
  • This strategy does not works if we click on another button,link.The current link does not gets closed – Saurabh Dec 17 '15 at 11:38
9

stopPropagation has to be called on an event object, not the element itself. Here's an example:

compile: function (elm) {
    return function (scope, elm, attrs) {
        $(elm).click(function (event) {
            event.stopPropagation();
        });
    };
}
Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
  • Thanks. I edited the question so that the directive calls stopPropagation on event and not element. Didn't work. Found this: "Currently some directives (i.e. ng:click) stops event propagation. This prevents interoperability with other frameworks that rely capturing such events." - https://github.com/angular/angular.js/issues/259 – Robert Christian Jan 27 '13 at 06:15
  • So found this to be the fix: x – Robert Christian Jan 27 '13 at 06:21
1

Here's a simple, abstract directive to stop event propagation. I figure it might be useful to someone. Simply pass in the event you wish to stop.

<div stopProp="click"></div>

app.directive('stopProp', function () {
  return function (scope, elm, attrs) {
    elm.on(attrs.stopProp, function (event) {
        event.stopPropagation();
    });
  };
});
Vinny
  • 11
  • 1