Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

The Event Argument & stopPropagation

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Back to our mission: when I click a delete link, it works... but I hate that it puts that annoying # in my URL and scrolls me up to the top of the page. You guys have probably seen and fixed that a million times. The easiest way is by finding your listener function and - at the bottom - returning false:

... lines 1 - 61
{% block javascripts %}
... lines 63 - 64
<script>
$(document).ready(function() {
var $table = $('.js-rep-log-table');
$table.find('.js-delete-rep-log').on('click', function () {
console.log('todo delete!');
return false;
});
... lines 74 - 77
});
</script>
{% endblock %}

Go back, remove that pound sign, refresh, and click! Haha! Get outta here pound sign!

But woh, something else changed: we're also not getting the "row clicked" text anymore. If I click just the row, I get it, but if I click the delete icon, it only triggers the event on that element. What the heck just happened?

The Event (e) Listener Argument

Back up a step. Whenever a listener function is called, your browser passes it an event argument, commonly just named e:

... lines 1 - 61
{% block javascripts %}
... lines 63 - 64
<script>
$(document).ready(function() {
... lines 67 - 68
$table.find('.js-delete-rep-log').on('click', function (e) {
... lines 70 - 73
});
... lines 75 - 78
});
</script>
{% endblock %}

This e variable is packed with information and some functions. The most important is e.preventDefault():

... lines 1 - 61
{% block javascripts %}
... lines 63 - 64
<script>
$(document).ready(function() {
... lines 67 - 68
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
... lines 71 - 73
});
... lines 75 - 78
});
</script>
{% endblock %}

Another is e.stopPropagation():

... lines 1 - 61
{% block javascripts %}
... lines 63 - 64
<script>
$(document).ready(function() {
... lines 67 - 68
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
... lines 72 - 73
});
... lines 75 - 78
});
</script>
{% endblock %}

It turns out that when you return false from a listener function, it is equivalent to calling e.preventDefault() and e.stopPropagation(). To prove it, remove the return false and refresh:

... lines 1 - 61
{% block javascripts %}
... lines 63 - 64
<script>
$(document).ready(function() {
... lines 67 - 68
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
console.log('todo delete!');
});
... lines 75 - 78
});
</script>
{% endblock %}

Yep, same behavior: no # sign, but still no "row clicked" when we click the delete icon.

e.preventDefault() versus e.stopPropagation()

The e.preventDefault() says: don't do the default, browser behavior for this event. Normally, when you "click" a "link", your browser navigates to its href... which is a #. So cool, e.preventDefault() stops that! But e.stopPropagation() tells your browser to not bubble this event any further up the DOM tree. And that's probably not what you want. Do you really want your event listener to be so bold that it decides to prevent all other listeners from firing? I've literally never had a use-case for this.

So get rid of that pesky e.stopPropagation() and refresh again:

... lines 1 - 61
{% block javascripts %}
... lines 63 - 64
<script>
$(document).ready(function() {
... lines 67 - 68
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
console.log('todo delete!');
});
... lines 74 - 77
});
</script>
{% endblock %}

And things are back to normal!

You should use e.preventDefault() in most cases, but not always. Sometimes, like with a keyup event, if you call preventDefault(), that'll prevent whatever the user just typed from actually going into the text box.

Now, what else can this magical event argument help us with?

Leave a comment!

7
Login or Register to join the conversation
Default user avatar

I think I've used the stopPropagation() function before.

I had a table where each row had a delete button and each row was clickable as well, navigating to some other page. If you're clicking the delete button, you don't want to trigger the event that navigates to the page. I'm not sure if I used stopPropagation() for that but I think that function could be useful in that case.

1 Reply

Hey Johan!

I think that's indeed a valid use-case! If what you described is your requirements, I can't think of a better way of doing this. LOVE it - thanks for commenting :).

Cheers!

2 Reply
Vuong N. Avatar
Vuong N. Avatar Vuong N. | posted 3 years ago | edited

there what's font name and font size you using in Phpstorm ? Thanks teacher.

Reply
Default user avatar
Default user avatar Jozef Repáň | posted 5 years ago

what about using href="javascript:void(0)" instead of href="#" in jQuery handled links?

Reply

Hey Jozef,

Yes, you can use it as well. But actually, it's something different than href="#" so we can't compare it. Actually, it's more similar to "event.preventDefault();". because without preventDefault() you will add '#' to the end of the current URL. But I think, event.preventDefault() is more proper way than using href="javascript:void(0)" nowadays and worth you nothing if you handle clicks with JS code - just add e.preventDefault() to the beginning of your handler.

Cheers!

1 Reply
Rafael F. Avatar
Rafael F. Avatar Rafael F. | posted 5 years ago

Hello guys.

Could you please explain the difference between e.stopPropagation() and e.stopImmediatePropagation() ? One time i also so a code like that.

$(document).on("submit", "form", function(e){
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
[......]

});

Is this really necessary? Thanks..

Reply

Hey Rafael F.

What "e.stopPropagation()" does is to stop any other *parent* handlers from being executed. and "e.stopImmediatePropagation()" stops *all* handlers. Give it a check to the code snippet shown in this answer: https://stackoverflow.com/a...

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony... but since it's a JavaScript tutorial, the concepts are still ? valid!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.1.*", // v3.1.10
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.1
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
        "symfony/swiftmailer-bundle": "^2.3", // v2.4.0
        "symfony/monolog-bundle": "^2.8", // 2.12.0
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0@dev", // dev-master
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.1
        "symfony/phpunit-bridge": "^3.0" // v3.1.6
    }
}
userVoice