The DOMContentLoaded event fires when the HTML document is fully parsed and deferred scripts have run. Learn how it differs from load, when to guard with document.readyState, why there is no onDOMContentLoaded property, and how to use it safely—with five examples and try-it labs.
01
Kind
Document event
02
Type
Event
03
Cancelable
No
04
Status
Baseline · Widely available
05
Handler prop
None (use addEventListener)
06
Waits for images?
No
Fundamentals
Introduction
Most beginner bugs look like this: your script runs in <head>, calls document.getElementById(...), and gets null because the element is not in the DOM yet. DOMContentLoaded is the classic signal that says: the HTML tree is ready to query.
It does not mean every image finished downloading. For a fully loaded page (images, styles, and other sub-resources), use the window load event instead. MDN calls mixing those up a common mistake.
💡
Simple alternative
Often you can skip the listener entirely: put your script at the end of <body>, just before </body>. By then the DOM above the script already exists.
Concept
Understanding DOMContentLoaded
A standard Document event. Per MDN it fires when:
The HTML document has been completely parsed.
Deferred scripts (defer and type="module") have downloaded and executed.
It does not wait for images, subframes, or async scripts.
It does not wait for stylesheets by itself—but deferred scripts wait for stylesheets, and DOMContentLoaded is queued after those deferred scripts.
It is not cancelable.
There is noonDOMContentLoaded handler property.
Compare
⚖️ DOMContentLoaded vs load vs readyState
API
Meaning
Use when
DOMContentLoaded
HTML parsed; deferred scripts done
You need to query/update the DOM ASAP
windowload
Full page including images & sub-resources
You truly need everything finished
document.readyState
loading / interactive / complete
Guard late/async scripts; related state machine
readystatechange
Fires whenever readyState changes
Fine-grained loading timeline logging
Foundation
📝 Syntax
Use the event name with addEventListener (MDN: there is no onDOMContentLoaded property):
document.addEventListener("DOMContentLoaded", (event) => {
console.log("DOM fully loaded and parsed");
});
Gotcha
⚠️ What If the Event Already Fired?
If your script runs with async, is injected later, or resumes after await, DOMContentLoaded may have already happened. A listener alone would never run. MDN’s safe pattern checks document.readyState first:
JavaScript
function doSomething() {
console.info("DOM loaded");
}
if (document.readyState === "loading") {
// Loading hasn't finished yet
document.addEventListener("DOMContentLoaded", doSomething);
} else {
// DOMContentLoaded has already fired
doSomething();
}
There is no race between the if check and addEventListener: JavaScript runs to completion in the current turn, so the document cannot flip to “loaded” between those two lines.
DOM fully loaded and parsed
Found h1: Hello DOMContentLoaded
How It Works
The browser finishes parsing the HTML (and deferred scripts), then fires the event. Inside the handler you can safely query elements that appear earlier in the document.
Example 2 — readyState Guard for Late Scripts
Always-safe setup for async, module, or injected scripts (MDN pattern).
JavaScript
function init() {
console.log("Setup running");
console.log("readyState:", document.readyState);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
Setup running
readyState: interactive
(or complete, depending on when the script runs)
How It Works
If parsing is still underway, wait for the event. If the document is already interactive or complete, run setup immediately so you do not miss the moment.
📈 Timing & Comparison
Contrast with load, see how sync work delays the event, and log the timeline.
Example 3 — DOMContentLoaded vs load
Both fire on a normal page load; DOMContentLoaded almost always comes first.
Listeners registered
1) DOMContentLoaded — DOM ready
2) load — full page (images, etc.)
How It Works
Use DOMContentLoaded for DOM work. Reserve load for cases that truly need images and other sub-resources finished.
Example 4 — Sync Script Delays Parsing
MDN idea: a long synchronous script in the document delays parsing, so the event fires later.
JavaScript
document.addEventListener("DOMContentLoaded", () => {
console.log("DOM fully loaded and parsed");
});
console.log("Heavy sync work starting...");
// Smaller loop for demos (MDN shows a huge loop to make the delay obvious)
let total = 0;
for (let i = 0; i < 5_000_000; i++) {
total += i % 3;
}
console.log("Heavy sync work done, total hint:", total % 10);
// While this ran, the HTML parser was blocked — DOMContentLoaded waits.
Document DOMContentLoaded is marked Baseline Widely available on MDN (since July 2015). Logos use the shared browser-image-sprite.png sprite from this project. It is a standard Document event used across modern browsers.
✓ Baseline · Widely available
Document DOMContentLoaded
Fires when the HTML is fully parsed and deferred scripts have run. Prefer it over load when you only need the DOM.
UniversalWidely available
Google ChromeFull support · Desktop & Mobile
Full support
Mozilla FirefoxFull support · Desktop & Mobile
Full support
Apple SafariFull support · macOS & iOS
Full support
Microsoft EdgeFull support · Chromium & Legacy
Full support
OperaFull support · Modern versions
Full support
Internet ExplorerSupported in IE9+ (prefer modern browsers)
Legacy
DOMContentLoadedExcellent
Bottom line: Use addEventListener("DOMContentLoaded", …) for DOM-ready setup. Guard async/late scripts with document.readyState. Use window load only when you need images and other sub-resources too.
Wrap Up
Conclusion
DOMContentLoaded is the standard “DOM is ready” event. Use it to start interacting with markup without waiting for every image. Pair it with a readyState guard when your script might run late, and reach for load only when you truly need a fully loaded page.
Prefer defer / modules / end-of-body scripts to reduce waiting
Keep handlers short so first interaction stays snappy
Use load when image dimensions or full assets matter
❌ Don’t
Wait for load when DOMContentLoaded is enough
Expect an onDOMContentLoaded property (it does not exist)
Assume a late async script will still see the event without a guard
Block the parser with huge sync scripts in <head>
Treat “DOM ready” as “every image finished”
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about DOMContentLoaded
DOM ready signal — faster than waiting for full page load.
5
Core concepts
📄01
HTML parsed
DOM ready
Event
📷02
Not images
use load later
Scope
🔒03
readyState
guard late scripts
Safety
🚫04
No on*
addEventListener
API
🚀05
Baseline
use in production
Compat
❓ Frequently Asked Questions
It fires on Document when the HTML has been completely parsed and deferred scripts (defer and type="module") have downloaded and executed. It does not wait for images, subframes, or async scripts.
No. MDN marks it Baseline Widely available (since July 2015). It is a standard Document event — not Deprecated, Experimental, or Non-standard.
No. MDN notes there is no onDOMContentLoaded event handler property. Use document.addEventListener("DOMContentLoaded", handler).
DOMContentLoaded means the DOM is ready. The window load event waits for the full page including images and other sub-resources. Prefer DOMContentLoaded when you only need the DOM.
Check document.readyState. If it is still "loading", add a DOMContentLoaded listener; otherwise run your setup immediately. This matters for async scripts, dynamic imports, and injected scripts.
No. It is a generic Event and is not cancelable.
Did you know?
Many developers still wrap everything in window.onload out of habit. That waits for images too—often slowing first interaction for no reason. If your code only needs elements in the markup, DOMContentLoaded (or a script at the end of body) is the better default.