Why DOMContentLoaded handler can block first paint?
There is a similar question that wasn't resolved.
I've encountered a situation when handler that listens to DOMContentLoaded can block first paint. Sometimes it blocks, sometimes it doesn't
I tried many times cmd + R to see it. Is there any explanation to this behaviuor?
Also I recordered a video to show this: https://www.youtube.com/watch?v=EDZQ1nLCK2w&feature=youtu.be
- When you see a blank page after reload then it means DOMContentLoaded blocked first paint
- When you see the text "Some text" and then a blank page after reload it means DOMContentLoaded didn't block first paint
window.addEventListener('DOMContentLoaded', () => {
let i = 0;
while (i++ < 1000000000) {
continue;
}
document.getElementById('el').remove();
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p id="el">Some text</p>
</body>
</html>
It's a race condition. Sometimes the document is already loaded/interactive before you add the listener.
You can see the difference by inspecting the document's readyState: document.readyState. Your code isn't running because sometimes the state is already interactive
or complete
, meaning that the DOMContentLoaded
event has already fired before you've actually added the listener.
The way to handle it would be something like this:
function init() {
let i = 0;
while (i++ < 1000000000) {
continue;
}
document.getElementById('el').remove();
}
if (document.readyState === 'loading') {
// Document not yet loaded, so wait for it.
window.addEventListener('DOMContentLoaded', init);
} else {
// Document is ready (interactive or complete), so call init immediately.
init();
}