How to wait for the DOM ready event in plain JavaScript
By Flavio Copes
Learn how to run plain JavaScript as soon as the page is ready by listening for the DOMContentLoaded event on the document with addEventListener.
To run JavaScript as soon as the DOM is ready, add an event listener to the document object for the DOMContentLoaded event:
document.addEventListener('DOMContentLoaded', (event) => {
//the event occurred
})
The browser fires DOMContentLoaded when it has finished parsing the HTML and built the DOM tree. At that point every element on the page exists, so document.querySelector() will find it. This is what you want when your script runs in the head, before the elements it needs.
The event does not wait for images or stylesheets to finish downloading. If you need everything loaded, listen for the load event on window instead:
window.addEventListener('load', (event) => {
//the whole page is loaded, images included
})
load fires much later on a page with heavy images, so prefer DOMContentLoaded unless you need the assets.
I usually don’t use arrow functions for event callbacks, because we cannot access this.
In this case we don’t need to, because this is always document. In any other event listener I would just use a regular function:
document.addEventListener('DOMContentLoaded', function (event) {
//the event occurred
})
for example if I’m adding the event listener inside a loop and I don’t really know what this will be when the event is triggered.
What if the DOM is already loaded?
An event listener only fires for future events. If your script runs after DOMContentLoaded already fired, the callback never runs, and nothing tells you why. This happens when a script is injected dynamically, or loaded late.
The fix is to check document.readyState first:
const setup = () => {
//work with the DOM
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setup)
} else {
setup()
}
readyState is 'loading' while the browser is still parsing the HTML. Once parsing is done it becomes 'interactive', and 'complete' after the full page load. So if it’s anything other than 'loading', the DOM is already there and we call setup() directly.
Do you always need this event?
Often you don’t. A script tag with the defer attribute runs after parsing finished, right before DOMContentLoaded. Module scripts (type="module") are deferred by default. A script tag at the end of the body also runs after the elements above it exist.
In all those cases the DOM is ready when your code runs, and you can skip the event listener entirely.
Related posts about platform: