JavaScript Events: Save the Bubbles!
The more we work with advanced, accessible, and crawlable web applications, the more control we need over element events. Mouseenter/leave events, keypress events, and the classic click event are probably the most-listened to events. Unfortunately many people, including myself, have been incorrectly handling event stoppage. In short: a majority of JavaScript framework users are killing the bubbles without knowing it.
There are two main methods involved in "stopping" event actions: Event.preventDefault
and Event.stopPropagation
. There is a huge difference in what these two methods accomplish, which you will see below. You'll also learn that blindly using JavaScript framework "stop" methods could lead to huge problems in your web application!
Event.preventDefault
The preventDefault method prevents an event from carrying out its default functionality. For example, you would use preventDefault
on an A element to stop clicking that element from leaving the current page:
//clicking the link will *not* allow the user to leave the page myChildElement.onclick = function(e) { e.preventDefault(); console.log('brick me!'); }; //clicking the parent node will run the following console statement because event propagation occurs logo.parentNode.onclick = function(e) { console.log('you bricked my child!'); };
While the element's default functionality is bricked, the event continues to bubble up the DOM.
Event.stopPropagation
The second method, stopPropagation
, allows the event's default functionality to happen but prevents the event from propagating:
//clicking the element will allow the default action to occur but propagation will be stopped... myChildElement.onclick = function(e) { e.stopPropagation(); console.log('prop stop! no bubbles!'); }; //since propagation was stopped by the child element's onClick, this message will never be seen! myChildElement.parentNode.onclick = function(e) { console.log('you will never see this message!'); };
stopPropagation
effectively stops parent elements from knowing about a given event on its child.
Dojo's dojo.stopEvent & MooTools' Event.stop
Here is where you can get into trouble: using a framework's custom "stop". Each framework has one but they all basically do the same thing:
//in mootools.... Event.stop = function(){ //does both! return this.stopPropagation().preventDefault(); } //mootools usage myElement.addEvent('click',function(e){ //stop the event - no propagation, no default functionality e.stop(); }); //in Dojo dojo.stopEvent = function(/*Event*/ evt){ // summary: // prevents propagation and clobbers the default action of the // passed event // evt: Event // The event object. If omitted, window.event is used on IE. evt.preventDefault(); evt.stopPropagation(); // NOTE: below, this method is overridden for IE } //dojo usage dojo.connect(myElement,'onclick',function(e){ //stop the event - no propagation, no default functionality dojo.stopEvent(e); });
The method executes both preventDefault
and stopPropagation
where chances are you only care about preventing the default functionality. I recently ran into this issue with a Dojo plugin. After exploring the source code, I quickly realized that both preventDefault
and stopPropagation
were being called, and all that was needed was preventDefault
. When I updated the source to simply use preventDefault
, every subsequent piece was working as it should!
Save The Bubbles!
While a simple stop method allows us to quickly handle events, it's important to think about what exactly you want to happen with bubbling. I'd bet that all a developer really wants is preventDefault
90% of the time! Incorrectly "stopping" an event could cause you numerous troubles down the line; your plugins may not work and your third party plugins could be bricked. Or worse yet -- your code breaks other functionality on a site. Save the bubbles!
How do you think this compares to the ugly way of just return false or return true (on
click
events)? Would this act more likestopPropagation
orpreventDefault
?I’m sure using the
event.preventDefault
is the ideal method, but I know I’ve justreturn false;
a million times!damnit, I thought this had something to do with West Ham!
If this had anything to do with West Ham, it would be detailing how shit they are. #arsenal
I KNOW! We’re so bad we couldn’t even beat Sunderland away. Oh hang on, that was #arsenal ;)
Wow what a nice explanation… It’s really helpful for me. Thanks David.
i am having some trouble with this, my class has the following code
els.addEvent('click',this.getInstance.bindWithEvent(this));
…
but no matter which of the event functions i try (
e.stopPropagation()
,e.stop()
,e.preventDefault()
)e.target
is always the child element (in this case animg
tag) when I want the parent element ( which is adiv
, and which is the element the event is actually attached to)… any thoughts?Some info regarding difference between : preventDefault(), stopPropagation(), stopImmediatePropagation(), return false
http://markupjavascript.blogspot.in/2013/10/event-bubbling-how-to-prevent-it.html
Interesting. jQuery has similar issue regarding to
return false
interpretation (more info at http://coding.smashingmagazine.com/2014/01/13/better-javascript-library-for-the-dom/, see “ISSUES WITH RETURN FALSE” section).Frankly I have no used why library authors decided to stop propagation. Performance? But they break flexibility, which is more important in my opinion.
We have inherited a mess due to lack of early standardization of browsers. Preventing default event handling has involved Event functions
stopEvent
,stopPropagation
,stopImmediatePropagation
, andpreventDefault
, andEvent
propertiescancelBubble
andreturnValue
, while event propagation has gone up either towindow
ordocument
or both. I’m going to stick with thepreventDefault
function, as recommended here, until I find a problem with it.