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!

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
        "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
    }
}