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.