I couldn't find much useful information on the subject so I decided to do some tests. I set up a Node server that would:
- Send the start of an html document
- Wait 5 seconds
- Send the rest of the html which includes an image
I then recorded the status of document.ready
and DOMContentLoaded
at each stage. My code:
var http = require('http');
var server = http.createServer(function(req, res) {
// Send the first part of the html
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(
'<!doctype html>' +
'<html lang="en">' +
'<head>' +
'<meta charset="utf-8">' +
'<meta http-equiv="x-ua-compatible" content="ie=edge">' +
'<title>JS Ready Test</title>' +
'<meta name="description" content="">' +
'<meta name="viewport" content="width=device-width, initial-scale=1">' +
'<script>' +
'console.log(document.readyState);' +
'document.onreadystatechange = function () {' +
'console.log(document.readyState);' +
'};' +
'document.addEventListener("DOMContentLoaded", function() {' +
'console.log("DOMContentLoaded");' +
'});' +
'</script>' +
'</head>' +
'<body>');
// Send a bunch of blank spaces so that the browser will load the buffer, if the buffer is too small the browser will wait for more data
var str = 'Start';
for (var i = 0; i < 2000; i++){
str += ' ';
}
res.write(str);
// Wait 5 seconds and send the rest of the data
setTimeout(function () {
res.write('Finish<img src="https://upload.wikimedia.org/wikipedia/commons/3/3d/LARGE_elevation.jpg"></body></html>');
res.end();
}, 5000);
});
// Listen on port 3000
server.listen(3000);
Results from my tests
First Buffer
Chrome (v43) / FF (v39) / IE11: document.ready === 'loading'
IE9 / IE10: document.ready === 'interactive'
Final buffer
Chrome / FF / IE11: document.ready === 'interactive'
, DOMContentLoaded
called
IE9 / IE10: No change in document.ready
, DOMContentLoaded
called
Sub-resources finish loading (in this case the image)
Chrome / FF / IE11: document.ready === 'complete'
IE9 / IE10: document.ready === 'complete'
As you can see IE9 & IE10 set document.ready === 'interactive'
too early.
Some possible solutions
1. Ignore IE9 / IE10
if (document.readyState === 'interactive' || document.readyState === 'complete') {
callback();
} else {
document.addEventListener('DOMContentLoaded', callback);
}
2. Add the DOMContentLoaded
in the <head>
of your document outside of your async script. This ensures that it will be attached before it is called.
// In <head>
<script>
var pageLoaded = false;
document.addEventListener('DOMContentLoaded', function() {
pageLoaded = true;
});
</script>
// In script.js
if (pageLoaded) {
callback();
} else {
document.addEventListener('DOMContentLoaded', callback);
}
3. Fallback to the load
event on `window.
if (document.readyState === 'complete') {
callback();
} else {
// You would need to add a safety so that your functions don't get called twice
document.addEventListener('DOMContentLoaded', callback);
window.addEventListener( "load", callback);
}