Document: DOMContentLoaded event

The DOMContentLoaded event fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.

A different event, load, should be used only to detect a fully-loaded page. It is a common mistake to use load where DOMContentLoaded would be more appropriate.

Synchronous JavaScript pauses parsing of the DOM. If you want the DOM to get parsed as fast as possible after the user has requested the page, you can make your JavaScript asynchronous and optimize loading of stylesheets. If loaded as usual, stylesheets slow down DOM parsing as they're loaded in parallel, "stealing" traffic from the main HTML document.

This event is not cancelable.


Use the event name in methods like addEventListener(), or set an event handler property.

addEventListener("DOMContentLoaded", (event) => {});

onDOMContentLoaded = (event) => {};

Event type

A generic Event.


Basic usage

document.addEventListener("DOMContentLoaded", (event) => {
  console.log("DOM fully loaded and parsed");

Delaying DOMContentLoaded

  document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");

  for (let i = 0; i < 1_000_000_000; i++);
  // This synchronous script is going to delay parsing of the DOM,
  // so the DOMContentLoaded event is going to launch later.

Checking whether loading is already complete

DOMContentLoaded may fire before your script has a chance to run, so it is wise to check before adding a listener.

function doSomething() {"DOM loaded");

if (document.readyState === "loading") {
  // Loading hasn't finished yet
  document.addEventListener("DOMContentLoaded", doSomething);
} else {
  // `DOMContentLoaded` has already fired

Note: There's no race condition here — it's not possible for the document to be loaded between the if check and the addEventListener() call. JavaScript has run-to-completion semantics, which means if the document is loading at one particular tick of the event loop, it can't become loaded until the next cycle, at which time the doSomething handler is already attached and will be fired.

Live example


<div class="controls">
  <button id="reload" type="button">Reload</button>

<div class="event-log">
  <label for="eventLog">Event log:</label>


const log = document.querySelector(".event-log-contents");
const reload = document.querySelector("#reload");

reload.addEventListener("click", () => {
  log.textContent = "";
  setTimeout(() => {
  }, 200);

window.addEventListener("load", (event) => {
  log.textContent += "load\n";

document.addEventListener("readystatechange", (event) => {
  log.textContent += `readystate: ${document.readyState}\n`;

document.addEventListener("DOMContentLoaded", (event) => {
  log.textContent += "DOMContentLoaded\n";



HTML Standard
# stop-parsing

Browser compatibility

BCD tables only load in the browser

See also