O'Reilly

JavaScript Events: Save the Bubbles!

By on  
JavaScript Bubbling

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!

Track.js Error Reporting

Upcoming Events

Recent Features

  • An Interview with Eric Meyer

    Your early CSS books were instrumental in pushing my love for front end technologies. What was it about CSS that you fell in love with and drove you to write about it? At first blush, it was the simplicity of it as compared to the table-and-spacer...

  • fetch API

    One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn't really made for what we've been using it for.  We've done well to create elegant APIs around XHR but we know we can do better.  Our effort to...

Incredible Demos

  • Image Manipulation with PHP and the GD Library

    Yeah, I'm a Photoshop wizard. I rock the selection tool. I crop like a farmer. I dominate the bucket tool. Hell, I even went as far as wielding the wizard wand selection tool once. ...OK I'm rubbish when it comes to Photoshop....

  • 5 More HTML5 APIs You Didnât Know Existed

    The HTML5 revolution has provided us some awesome JavaScript and HTML APIs.  Some are APIs we knew we've needed for years, others are cutting edge mobile and desktop helpers.  Regardless of API strength or purpose, anything to help us better do our job is a...

Discussion

  1. How do you think this compares to the ugly way of just return false or return true (on click events)? Would this act more like stopPropagation or preventDefault?

    I’m sure using the event.preventDefault is the ideal method, but I know I’ve just return false; a million times!

  2. 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 ;)

  3. Wow what a nice explanation… It’s really helpful for me. Thanks David.

  4. craig

    i am having some trouble with this, my class has the following code
    els.addEvent(‘click’,this.getInstance.bindWithEvent(this));

    getInstance: function(e){
    e.stop();
    this.instance = e.target;
    this.addContainerEl();

    }

    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 an img tag) when I want the parent element ( which is a div, and which is the element the event is actually attached to)… any thoughts?

  5. Some info regarding difference between : preventDefault(), stopPropagation(), stopImmediatePropagation(), return false
    http://markupjavascript.blogspot.in/2013/10/event-bubbling-how-to-prevent-it.html

  6. 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.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • OâReilly Velocity Conference â New York

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...

  • Free Download: Font Bundle Featuring 17 Incredible Typefaces

    The only thing we love more than a good font, is a good free font. So we’ve combed the Web for some of our favorite free fonts, and gathered them here in a single download. You’ll find a variety of useful typefaces, from highly geometric designs...

  • OâReilly Velocity Conference â Amsterdam

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...

  • CanIUse Command Line

    Every front-end developer should be well acquainted with CanIUse, the website that lets you view browser support for browser features.  When people criticize my blog posts for not detailing browser support for features within the post, I tell them to check CanIUse:  always up to date, unlike...

  • Generating Alternative Stylesheets for Browsers Without @media

    If your CSS code is built with a mobile-first approach, it probably contains all the rules that make up the "desktop" view inside @media statements. That's great, but browsers that don't support media queries (IE 8 and below) will simply ignore them, ending up getting the...