preventDefault vs. stopPropagation vs. stopImmediatePropagation

May 20, 2015

Three similar and often confused methods. Let’s explore the different between:

  • Event.preventDefault()
  • Event.stopPropagation()
  • Event.stopImmediatePropagation()

Summary

First off, let’s check out the MDN Summaries.

  • preventDefault: Cancels the event if it is cancelable, without stopping further propagation of the event.
  • stopPropagation: Prevents further propagation of the current event.
  • stopImmediatePropagation: Prevents other listeners of the same event from being called.

Event.preventDefault

Let’s look at a code example. We know that clicking the submit button on a form submits it to the form handler. Event.preventDefault is a perfect way to not submit a form when the submit button is clicked.

<form id="myForm" action="/my-handling-form-page" method="post">
    <div>
        <label for="name">Name:</label>
        <input type="text" id="name" />
    </div>
    <div>
        <label for="mail">E-mail:</label>
        <input type="email" id="mail" />
    </div>
    <div>
        <label for="msg">Message:</label>
        <textarea id="msg"></textarea>
    </div>
    
    <div class="button">
        <button type="submit">Send your message</button>
    </div>
</form>
$('#myForm').on('submit', function(e) {
    e.preventDefault(); // Now nothing will happen
});

Event.preventDefault will ensure that the form is never submitted, but it won’t control or prevent that submit or click event from bubbling up. That’s what the other two are for.

Event.stopPropagation

stopPropagation ensures that the event doesn’t bubble any further. Let’s look at another code example:

<div class="container">
    <a href="#" class="element">Click Me!</a>
</div>
$('.container').on('click', function(e) {
    console.log('container was clicked');
});

$('.element').on('click', function(e) {
    e.preventDefault(); // Now link won't go anywhere
    console.log('element was clicked');
});

Now if you were to click the link with the console open, you would see:

"element was clicked"
"container was clicked"

Now let’s add Event.stopPropagation:

$('.container').on('click', function(e) {
    console.log('container was clicked');
});

$('.element').on('click', function(e) {
    e.preventDefault(); // Now link won't go anywhere
    e.stopPropagation(); // Now the event won't bubble up
    console.log('element was clicked');
});

And click the link again. This time we see:

"element was clicked"

Event.stopImmediatePropagation

This gets you 90% of everything you’ll need as far as manipulating events. But let’s pose a last use case that can prove difficult.

We’ll start off with similar markup, except we’ll give the anchor two classes. A generic one, item, that all anchors in this area will get, and a specific one, element, that’s very important for our application to work.

<div class="container">
    <a href="#" class="item element">Click Me!</a>
</div>

And we’ll add our nifty Event.stopPropagation we learned about in the last section!

$('.item').on('click', function(e) {
    console.log('an item was clicked');
});

$('.element').on('click', function(e) {
    e.preventDefault(); // Now link won't go anywhere
    e.stopPropagation(); // Now the event won't bubble up
    console.log('element was clicked');
});

But, when we click on the element we see:

"an item was clicked"
"element was clicked"

The problem here is that item and element are evenly ranked in the DOM. It’s not as though it hits element and then bubbles up to container like we saw in the last example. Since the click event fires on both element and item at the same time, you cannot stopPropagation like you’d expect.

This is where stopImmediatePropagation comes in handy!

$('.element').on('click', function(e) {
    e.preventDefault(); // Now link won't go anywhere
    e.stopImmediatePropagation(); // Now item on click won't fire
    console.log('element was clicked');
});

$('.item').on('click', function(e) {
    console.log('an item was clicked');
});

Now one important thing to note here is: Battles over immediate propagation go to the first one declared in your script ( or series of scripts ). So as you may have noticed, in order to get stopImmediatePropagation working we had to switch the listeners so that element comes before item!

Let’s run it one last time and we should see:

"element was clicked"