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.
The following code ports the functionality of the DOMContentLoaded
event all the way back to IE6+, with a fallback to window.onload
that works everywhere.
function DOMContentLoaded() { "use strict";
var ael = 'addEventListener', rel = 'removeEventListener', aev = 'attachEvent', dev = 'detachEvent';
var alreadyRun = false,
funcs = arguments;
function microtime() { return + new Date() }
var jscript_version = Number( new Function("/*@cc_on return @_jscript_version; @*\/")() );
if (document.readyState === 'complete') { ready(null); return; }
if (jscript_version < 9) { doIEScrollCheck(); return; }
if (document[ael]) {
document[ael]("DOMContentLoaded", ready, false);
window[ael]("load", ready, false);
} else
if (aev in window) { window[aev]('onload', ready);
} else {
addOnload(ready);
}
function addOnload(fn) {
var prev = window.onload;
if ( typeof addOnload.queue !== 'object') {
addOnload.queue = [];
if (typeof prev === 'function') {
addOnload.queue.push( prev );
}
}
if (typeof fn === 'function') { addOnload.queue.push(fn) }
window.onload = function() {
for (var i = 0; i < addOnload.queue.length; i++) { addOnload.queue[i]() }
};
}
function dequeueOnload(fn, all) {
if (typeof addOnload.queue === 'object') {
for (var i = addOnload.queue.length-1; i >= 0; i--) {
if (fn === addOnload.queue[i]) {
addOnload.queue.splice(i,1); if (!all) {break}
}
}
}
}
function ready(ev) {
if (alreadyRun) {return} alreadyRun = true;
var readyTime = microtime();
detach();
for (var i=0; i < funcs.length; i++) {
var func = funcs[i];
if (typeof func === 'function') {
func.call(document, {
'readyTime': (ev === null ? null : readyTime),
'funcExecuteTime': microtime(),
'currentFunction': func
});
}
}
}
function detach() {
if (document[rel]) {
document[rel]("DOMContentLoaded", ready); window[rel]("load", ready);
} else
if (dev in window) { window[dev]("onload", ready); }
else {
dequeueOnload(ready);
}
}
function doIEScrollCheck() {
if ( window.frameElement ) {
try { window.attachEvent("onload", ready); } catch (e) { }
return;
}
try {
document.documentElement.doScroll('left');
} catch(error) {
setTimeout(function() {
(document.readyState === 'complete') ? ready() : doIEScrollCheck();
}, 50);
return;
}
ready();
}
}
document.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM fully loaded and parsed');
});
<script>
document.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM fully loaded and parsed');
});
for( let i = 0; i < 1000000000; i++)
{}
</script>
DOMContentLoaded
may fire before your script has a chance to run, so it is wise to check before adding a listener.
function doSomething() {
console.info('DOM loaded');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', doSomething);
} else {
doSomething();
}
HTML
<div class="controls">
<button id="reload" type="button">Reload</button>
</div>
<div class="event-log">
<label>Event log:</label>
<textarea readonly class="event-log-contents" rows="8" cols="30"></textarea>
</div>
JS
const log = document.querySelector('.event-log-contents');
const reload = document.querySelector('#reload');
reload.addEventListener('click', () => {
log.textContent ='';
window.setTimeout(() => {
window.location.reload(true);
}, 200);
});
window.addEventListener('load', (event) => {
log.textContent = log.textContent + 'load\n';
});
document.addEventListener('readystatechange', (event) => {
log.textContent = log.textContent + `readystate: ${document.readyState}\n`;
});
document.addEventListener('DOMContentLoaded', (event) => {
log.textContent = log.textContent + `DOMContentLoaded\n`;
});
Result