If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeBack 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?
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.
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?
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!
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!
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..
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!
// 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
}
}
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.