Why you shouldn’t return false in MooTools event handlers
Saturday, July 25th, 2009 by Sebastian MarkbågeLet’s say I have a link (anchor tag with href), and I wish to attach an event listener to it.
<ul> <li><a id="mylink" href="http://...">my link</a></li> </ul> |
document.id('mylink').addEvent('click', function(){ console.log('hello world'); }); |
Now, if I click the link it will log the message but the browser window will also visit the location of the link. There are a bunch of such default behaviors to pretty much every event in the DOM. If we’re implementing custom behavior, we typically want to prevent this default behavior. A common practise is to have the method return false as such:
document.id('mylink').addEvent('click', function(){ console.log('hello world'); return false; }); |
THIS IS BAD! Don’t. To understand the reason for this, you need to understand event bubbling and the difference between preventDefault and stopPropagation.
Event bubbling and stopPropagation
When an event is dispatched, it first fires the listeners of the ‘mylink’ element (not quite true, but we don’t use capture). But then it propagates (bubbles) up to the LI-element, UL-element, BODY-element etc. So for every click on any element, the ‘click’ event is triggered on the BODY-element. After all of that, the default behavior of the browser is triggered.
In most browsers bubbling continues to the document and window objects, but that’s not always true for IE.
This is a powerful model. It allows us to do things like Event delegation. You can place a listener on the UL-element to catch any events triggered on the LI-elements without adding listeners to all the existing or any new LI-elements.
Sometimes we don’t want bubbling to occur. Let’s say for example that I wanted to have a ‘click’ event handler on the UL-element that handles clicks on the UL area outside of any A-element. Then I could accept the Event object as the first parameter, use stopPropagation during the click event on the A-element to stop the event before it reaches the UL.
document.getElements('ul').addEvent('click', function(){ console.log('You clicked within the UL but outside of any link.'); }); document.getElements('a').addEvent('click', function(event){ console.log('You clicked a link.'); event.stopPropagation(); }); |
preventDefault
In my example above the browser would still visit the href of the link. Stopping propagation (bubbling) doesn’t actually prevent the default browser action. So we also need to call preventDefault during the click event to prevent the default operation of clicking a link.
document.getElements('a').addEvent('click', function(event){ console.log('You clicked a link.'); event.stopPropagation(); event.preventDefault(); }); |
Now since this is fairly common MooTools has a shortcut for doing both stopPropagation AND preventDefault. Namely the stop() method:
document.getElements('a').addEvent('click', function(event){ console.log('You clicked a link.'); event.stop(); }); |
So, why is return false bad?
In the standard browser DOM model it’s equivalent to calling event.preventDefault(); but in MooTools it’s equivalent to calling event.stop(); i.e. it also calls stopPropagation.
This is a problem. If you use this model routinely you may not notice that you actually prevent plugins attached to elements higher up in the bubbling chain.
Let’s say I want to use the ‘mouseleave’ event to hide the UL-element when the mouse leaves. If I also return false on the ‘mouseout’ event on the A-element, I may not get the ‘mouseleave’ event because the A-element stops it. OR maybe I have a plugin higher up that requires that my events bubble. It’ll be even more prevalent as more plugins makes use of Event delegation.
Therefore you need to be very explicit about when you stop propagation and not.
Second of all, the “return false” API doesn’t make sense. The function isn’t failing. It isn’t canceled. In fact, it’s canceling a DIFFERENT function.
Therefore you should ALWAYS be explicit by calling either event.preventDefault(), event.stopPropagation() or event.stop(); instead of relying on an implicit convention that differs between frameworks.
Returning a false value is a relic from the old days when we only had a single listener per event.
Binding Parameters
Sometimes you need to bind parameters that you wish to pass to an event listener. A common practise is to use bind.
document.getElements('a').addEvent('click', function(paramA, paramB){ // do something with this, paramA and paramB return false; }.bind(someObj, [objA, objB])); |
In this case you can’t accept an Event object since you’ve bound your parameters to other objects. In this case you can use bindWithEvent to let the first parameter (the event object) get through, while binding the remaining parameters.
document.getElements('a').addEvent('click', function(event, paramA, paramB){ // do something with this, paramA and paramB event.stop(); }.bindWithEvent(someObj, [objA, objB])); |
$lambda(false)
“But I don’t want to type out all of that just to stop an event. I like $lambda(false) to easily block events.”
People sometimes use the $lambda method to create a function that returns false to easily stop an event without doing anything else: el.addEvent(‘click’, $lambda(false));
So you need a method that does nothing other than accepts an Event object and calls preventDefault, stopPropagation or stop? Thanks to MooTools generics you can easily do that like this:
element.addEvent('click', Event.preventDefault); // OR... element.addEvent('click', Event.stopPropagation); // OR... element.addEvent('click', Event.stop); |
For you that think “return false;” saves bandwidth… “e,” and “e.stop();” is two bytes shorter.
Additional Event Listeners on the same Element
Neither preventDefault or stopPropagation or even an error prevents any additional handlers/listeners on the same element. So if you have two handlers listening to the same event, then both will be triggered regardless of the result of either function.
That should be true for all Events, even Class events. More on that in MooTools 2.0…
Tags: Events, JavaScript, MooTools
July 25th, 2009 at 03:12
Great post. Thanks for the tips. Need to start using event delegation more often.
July 25th, 2009 at 19:46
Thanks a lot for this post! Started updating a few scripts straight away.
Just came across a problem with event bubbling a couple of days ago, I could have solved a lot faster with this new knowledge.
July 30th, 2009 at 02:34
Very helpful – thank you!
Just a note; the mootools documentation includes this:
//Prevents a link Element from being clickable.
myLink.addEvent(‘click’, $lambda(false));
Not a bad thing, but I do think mootools needs more articles like this one
to explain how and WHY : )
August 20th, 2009 at 18:03
Hi
event.stop does nothing in Firefox 3 so it runs the # in the HREF.
event.preventDefault reports an error in IE 8 and therefore never runs the JS.
thanks
dM
September 2nd, 2009 at 22:14
@drifter
Do you report that errors?
March 31st, 2011 at 23:27
thanks on the side of this kindly tips 147896325
viagra – viagra
April 12th, 2011 at 20:33
I quite agree with your statement that we should be very explicit about when we stop propagation and not.
June 7th, 2011 at 20:56
Hi, I am glad to discover so many valuable information right here, it’s a fantastic website. Thanks alot : ) for this good posts! I’ll bookmark your web site and take the rss feeds also… I hope you can keep updating your blog posts often as you have one devoted subscriber here.
June 26th, 2012 at 11:30
Cheers good on the proficient information. Basically unprejudiced wen upward! My spouse and i routinely accomplish certainly not icreasing in those however, think about anyone does a new animatedly buddy-buddy despoile along with I’m unquestionable a lot of people suavity your a lesser amount of at just about any rate.
March 17th, 2013 at 03:47
is lined with leopard printed fabric. It ;s color is actually called Augergine, which I find really regal and gorgeous.You can get this from Net A Porter.Featuring cheap Louis Vuitton replica handbags http://louisvuittonneverfullreplica.blogspot.com/
April 26th, 2013 at 09:16
Hi there, I found your site by way of Google at the same time as searching for a comparable matter, your website got here up, it seems to be good. I’ve added to favourites|added to bookmarks.