Question

JavaScript DOM Ready: Vanilla JS Equivalent of jQuery $(document).ready()

htmljavascriptjquery

Question

How can I run JavaScript code as soon as the HTML document is ready, without using jQuery's $(document).ready()?

In jQuery, a common pattern is:

$(document).ready(function () {
  // code here
});

If I am writing plain JavaScript with no library, what is the proper way to execute one or more functions when the page is ready for DOM manipulation?

Possible alternatives I know about include:

window.onload = myFunction;

or:

<body onload="myFunction()">

or calling the function from a script near the end of the page:

<script>
  myFunction();
</script>

What is the correct cross-browser approach, including older and newer browsers, for behavior similar to jQuery's $.ready()?

Short Answer

By the end of this page, you will understand the difference between DOM ready and page fully loaded, how to use DOMContentLoaded in vanilla JavaScript, when window.onload is more appropriate, and how to write a safe helper that works whether the DOM is already loaded or not.

Concept

The main idea behind jQuery's $(document).ready() is simple: run code as soon as the DOM is ready to be used.

The DOM (Document Object Model) is the browser's in-memory representation of your HTML. If your script tries to access elements before the browser has parsed them, code like document.getElementById(...) may fail or return null.

In plain JavaScript, the closest equivalent is usually:

document.addEventListener('DOMContentLoaded', function () {
  // DOM is ready
});

Why this matters

There are two common "ready" moments in a page:

  • DOMContentLoaded: fires when the HTML has been parsed and the DOM is built
  • load: fires later, when the whole page including images, stylesheets, and other resources has finished loading

For most UI code, event wiring, and DOM manipulation, you want DOMContentLoaded, not load.

Why jQuery had ready()

Older browsers behaved inconsistently, so jQuery wrapped those differences in a simple API. Modern browsers support DOMContentLoaded, so plain JavaScript is enough in most projects.

Important practical detail

If your script is loaded after the DOM is already ready, adding a DOMContentLoaded listener at that moment will not help because the event has already fired. In that case, you should check document.readyState and either run immediately or attach the listener.

Mental Model

Think of the browser like a team setting up a room for a meeting:

  • HTML parsing is the team putting chairs, tables, and whiteboards into place
  • DOM ready means the room layout is finished, so people can start working inside it
  • Page load means everything else has also arrived, including decorations, projectors, and snacks

If your code just needs to sit people in chairs, you only need the room layout finished. You do not need to wait for every extra item to arrive.

So:

  • Use DOMContentLoaded when you need the structure of the page
  • Use load when you need all external resources too
Take Quiz

Syntax and Examples

Basic vanilla JavaScript equivalent

document.addEventListener('DOMContentLoaded', function () {
  myFunction();
});

This runs myFunction() as soon as the DOM is ready.

Example with a real DOM action

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>DOM Ready Example</title>
</head>
<body>
  <h1 id="title">Hello</h1>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
      const title = document.getElementById('title');
      title.textContent = 'The DOM is ready';
    });
  </script>
</body>
</html>

What this does

  • The browser parses the HTML
  • Once the DOM structure exists, DOMContentLoaded fires
  • The script finds the <h1> and changes its text

Safe helper that works before or after DOM ready

function onReady(callback) {
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', callback);
  } else {
    callback();
  }
}

onReady(function () {
  myFunction();
});

This is a good plain JavaScript replacement for many uses of jQuery ready().

When to use window.onload

window.addEventListener('load', function () {
  console.log('Everything is loaded, including images');
});

Use this only when your code depends on fully loaded resources, such as image dimensions or external assets.

Step by Step Execution

Traceable example

<div id="message"></div>

<script>
  function onReady(callback) {
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', callback);
    } else {
      callback();
    }
  }

  onReady(function () {
    const box = document.getElementById('message');
    box.textContent = 'Ready!';
  });
</script>

Step by step

  1. The browser reads the HTML.
  2. It creates the <div id="message"></div> element in the DOM.
  3. The script runs and defines the onReady function.
  4. onReady(...) is called with a callback function.
  5. The code checks document.readyState.
    • If the document is still loading, it waits for DOMContentLoaded.
    • If the DOM is already ready, it runs the callback immediately.
  6. The callback executes.
  7. document.getElementById('message') returns the <div>.
  8. The text content is changed to Ready!.

Why this is useful

This pattern avoids a common bug:

  • If you always attach DOMContentLoaded, but your script runs after the event already happened, your callback may never run.
  • Checking readyState makes the function reliable.

Real World Use Cases

Common uses of DOM ready code

Attach event listeners

document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('saveBtn').addEventListener('click', saveForm);
});

You need the button to exist before attaching the click handler.

Initialize UI components

document.addEventListener('DOMContentLoaded', function () {
  setupTabs();
  setupMenu();
});

Many apps initialize navigation, modals, accordions, or forms when the DOM is available.

Read values from the page

document.addEventListener('DOMContentLoaded', function () {
  const userId = document.getElementById('user-id').value;
  loadUserProfile(userId);
});

Start client-side rendering

In small apps, you may render content into a container as soon as the DOM exists.

Delay image-dependent logic until load

window.addEventListener('load', function () {
  console.log('Image sizes are now safe to measure');
});

This is useful for galleries, canvas drawing, or layout calculations that depend on external resources.

Take Quiz

Real Codebase Usage

How this is used in real projects

Developers usually do one of these:

1. Put scripts at the end of <body>

If a script only depends on elements already written above it, placing it near the end of the document often removes the need for a ready handler.

<body>
  <button id="btn">Click</button>

  <script>
    document.getElementById('btn').addEventListener('click', function () {
      console.log('Clicked');
    });
  </script>
</body>

2. Use defer for external scripts

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

A deferred script downloads in parallel and runs after HTML parsing is complete. This is a very common modern solution.

3. Use a small onReady helper

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

This is useful in shared utility code.

4. Use guard clauses for missing elements

In real codebases, not every page has every element.

onReady(function () {
  const form = document.getElementById('profile-form');
  if (!form) return;

  form.addEventListener('submit', validateProfile);
});

This prevents errors on pages where that form does not exist.

5. Split setup into small functions

onReady(function () {
  initSearch();
  initThemeToggle();
  initNotifications();
});

This keeps startup code organized and easier to test.

Common Mistakes

1. Using window.onload when only the DOM is needed

Broken idea:

window.onload = function () {
  setupMenu();
};

This works, but it waits longer than necessary.

Better:

document.addEventListener('DOMContentLoaded', function () {
  setupMenu();
});

2. Overwriting an existing onload handler

Broken code:

window.onload = firstFunction;
window.onload = secondFunction;

Only the second assignment remains.

Better:

window.addEventListener('load', firstFunction);
window.addEventListener('load', secondFunction);

3. Calling the function immediately instead of passing it

Broken code:

document.addEventListener('DOMContentLoaded', myFunction());

This runs the function right away and passes its result.

Correct:

document.addEventListener('DOMContentLoaded', myFunction);

or:

document.addEventListener('DOMContentLoaded', function () {
  myFunction();
});

4. Assuming DOMContentLoaded always fires after your script is added

Broken assumption:

document.addEventListener('DOMContentLoaded', init);

If this line runs after the event already fired, init may never run.

Safer:

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', init);
} else {
  init();
}

5. Using inline HTML event handlers

Less ideal:

<body onload="myFunction()">

Why avoid it:

  • mixes HTML and JavaScript
  • harder to maintain
  • less reusable

Prefer separate JavaScript files or script blocks.

6. Accessing elements before they exist

Broken code:

<script>
  document.getElementById('title').textContent = 'Hi';
</script>

<h1 id="title">Hello</h1>

At the moment the script runs, the <h1> has not been parsed yet.

Fix it with DOMContentLoaded, defer, or by moving the script lower in the page.

Comparisons

DOM ready vs full page load

ApproachWhen it runsBest forNotes
DOMContentLoadedAfter HTML is parsedDOM queries, event binding, UI setupClosest to jQuery ready()
loadAfter all resources finish loadingImage measurements, full asset readinessUsually later than needed
Script at end of <body>When parser reaches the scriptSimple pagesWorks if needed elements already appear above
defer scriptAfter HTML parsing, before DOMContentLoaded completesModern external scriptsClean modern solution

window.onload vs addEventListener('load', ...)

PatternDifference
window.onload = fnReplaces any previous handler
window.addEventListener('load', fn)Adds another handler safely

jQuery ready() vs vanilla JavaScript

jQueryVanilla JavaScript
$(document).ready(fn)document.addEventListener('DOMContentLoaded', fn)
Handles old browser quirks internallyYou use browser APIs directly
Requires jQueryNo library required

Which should you choose?

  • Choose DOMContentLoaded for most cases.
  • Choose load only if external resources matter.
  • Choose defer for modern script loading in production apps.
  • Choose a small onReady helper if your code may run before or after DOM readiness.
Take Quiz

Cheat Sheet

Quick reference

Vanilla JS equivalent of jQuery ready

document.addEventListener('DOMContentLoaded', function () {
  // DOM is ready
});

Safe reusable helper

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

Full page loaded

window.addEventListener('load', function () {
  // everything loaded
});

Modern external script loading

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

Rules of thumb

  • Use DOMContentLoaded for most DOM work.
  • Use load for images or other external resources.
  • Prefer addEventListener over assigning window.onload.
  • If a script may run late, check document.readyState.
  • Avoid inline onload in HTML when possible.

Common edge case

If the DOM is already loaded:

if (document.readyState !== 'loading') {
  init();
}

Otherwise:

document.addEventListener('DOMContentLoaded', init);

FAQ

What is the vanilla JavaScript equivalent of $(document).ready()?

Use:

document.addEventListener('DOMContentLoaded', function () {
  // code here
});

Is window.onload the same as DOMContentLoaded?

No. DOMContentLoaded fires when the DOM is built. load fires later, after images and other resources finish loading.

Can I just put my script at the end of the <body>?

Yes, often. If the elements your script needs already appear above it, that is a simple and valid approach.

What if my script runs after DOMContentLoaded already happened?

Check document.readyState and run immediately if the document is no longer loading.

Is body onload recommended?

Usually no. It works, but it mixes HTML and JavaScript and is harder to maintain than using addEventListener.

Do I still need a ready handler if I use defer?

Often no. Deferred scripts run after HTML parsing, so DOM elements are usually available.

Why did jQuery need ready()?

Older browsers had inconsistent behavior. jQuery provided a reliable cross-browser wrapper. Modern browsers support DOMContentLoaded directly.

Take Quiz

Mini Project

Description

Build a small page initializer in vanilla JavaScript that updates text on the page and attaches a button click handler as soon as the DOM is ready. This demonstrates the most common real use of DOM-ready logic: safely finding elements and wiring up interactions without relying on jQuery.

Goal

Create a reusable onReady helper and use it to initialize page content and event listeners safely.

Requirements

  • Create an onReady function that runs a callback when the DOM is ready.
  • Add a heading and a button in HTML.
  • Change the heading text during initialization.
  • Attach a click event to the button.
  • Show a message in the console when the button is clicked.
Take Quiz

Keep learning

Related questions

View more