Question
JavaScript DOM Ready: Vanilla JS Equivalent of jQuery $(document).ready()
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 builtload: 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
DOMContentLoadedwhen you need the structure of the page - Use
loadwhen you need all external resources too
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,
DOMContentLoadedfires - 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
- The browser reads the HTML.
- It creates the
<div id="message"></div>element in the DOM. - The script runs and defines the
onReadyfunction. onReady(...)is called with a callback function.- 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.
- If the document is still loading, it waits for
- The callback executes.
document.getElementById('message')returns the<div>.- 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
readyStatemakes 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.
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
| Approach | When it runs | Best for | Notes |
|---|---|---|---|
DOMContentLoaded | After HTML is parsed | DOM queries, event binding, UI setup | Closest to jQuery ready() |
load | After all resources finish loading | Image measurements, full asset readiness | Usually later than needed |
Script at end of <body> | When parser reaches the script | Simple pages | Works if needed elements already appear above |
defer script | After HTML parsing, before DOMContentLoaded completes | Modern external scripts | Clean modern solution |
window.onload vs addEventListener('load', ...)
| Pattern | Difference |
|---|---|
window.onload = fn | Replaces any previous handler |
window.addEventListener('load', fn) | Adds another handler safely |
jQuery ready() vs vanilla JavaScript
| jQuery | Vanilla JavaScript |
|---|---|
$(document).ready(fn) | document.addEventListener('DOMContentLoaded', fn) |
| Handles old browser quirks internally | You use browser APIs directly |
| Requires jQuery | No library required |
Which should you choose?
- Choose
DOMContentLoadedfor most cases. - Choose
loadonly if external resources matter. - Choose
deferfor modern script loading in production apps. - Choose a small
onReadyhelper if your code may run before or after DOM readiness.
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
DOMContentLoadedfor most DOM work. - Use
loadfor images or other external resources. - Prefer
addEventListenerover assigningwindow.onload. - If a script may run late, check
document.readyState. - Avoid inline
onloadin 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.
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
onReadyfunction 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.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.