jQuery Document Ready Equivalent in Vanilla JS

This article explains how to replicate jQuery's $(document).ready() function using standard, modern Vanilla JavaScript. In web development, ensuring that the Document Object Model (DOM) is fully loaded before executing JavaScript is crucial for preventing errors caused by trying to manipulate elements that do not yet exist on the page. You will learn the direct event-based equivalent in native JavaScript, the modern script loading approach that eliminates the need for ready handlers entirely, and a fallback method for asynchronous scripts.

The jQuery Pattern

In jQuery, code is typically wrapped in a ready block to wait for the DOM:

$(document).ready(function() {
    // DOM is ready
});

// Or the shorthand syntax:
$(function() {
    // DOM is ready
});

The Native Equivalent: DOMContentLoaded

The direct Vanilla JavaScript equivalent to $(document).ready() is the DOMContentLoaded event. It fires when the initial HTML document has been completely parsed and loaded, without waiting for external stylesheets, images, and subframes to finish loading.

document.addEventListener('DOMContentLoaded', () => {
    // DOM is ready, your code goes here
});

Modern Best Practice: The defer Attribute

Modern web development often avoids DOM ready event listeners entirely by using the defer attribute inside the <script> tag. When using defer, the browser downloads the script in parallel with parsing the HTML, but executes it only after the HTML document is fully parsed, immediately before DOMContentLoaded fires.

<head>
    <script src="app.js" defer></script>
</head>

When using defer, all code inside app.js runs as if it were wrapped in a DOM ready handler:

// Inside app.js with defer enabled:
const element = document.querySelector('.my-class');
element.textContent = 'Updated content';

Alternatively, placing a standard <script> tag at the very bottom of the <body> element achieves a similar effect, as all preceding DOM nodes are already parsed by the time the script runs.

Handling Edge Cases with readyState

If your script loads asynchronously (e.g., using async or injected dynamically), the DOMContentLoaded event may have already fired before your code executes. To handle this, check the document.readyState property:

function ready(fn) {
    if (document.readyState !== 'loading') {
        fn();
    } else {
        document.addEventListener('DOMContentLoaded', fn);
    }
}

// Usage
ready(() => {
    // Code executes immediately if DOM is ready, or waits if still loading
});

DOMContentLoaded vs. window.onload

Be careful not to confuse DOMContentLoaded with the load event (window.onload).

  • DOMContentLoaded (and jQuery's ready) triggers as soon as the HTML elements exist in memory.
  • window.addEventListener('load', ...) waits for the entire page to finish loading, including large images, stylesheets, and embedded frames.