When using defer
, the scripts will indeed execute after the page has been fully downloaded in the order that they appeared.
You can see here a schema representing the behavior.



In any case, scripts are always executed before DOMContentLoaded
, you can test that theory here :
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
alert("Alert from script")
});
$(document).ready(function() {
alert("Alert from jQuery")
});
</script>
<!-- Alerts "Alert from defer -->
<script defer="defer" type="text/javascript" src="https://pastebin.com/raw.php?i=5tF5s4mB"></script>
And unless you remove the alert from the ready state, all scripts will execute before them, otherwise they get executed in the order they appear in the DOM
.
So you can be sure that all code inside DOMContentLoaded
will be able to access the fully loaded DOM
.
On an end note, do watch out of defer
's compatibility across all browsers.
Script attributes async
and defer
, don’t block DOMContentLoaded
. JavaScript modules behave like defer
, they don’t block it too.
This can be tested using type="module"
<script type="module">
alert("Alert from script")
</script>
<script type="text/javascript">
alert("Page loaded")
</script>
As you can see, the page loads before executing any modules, no matter the order.