Reputation: 5335
I can't find a reliable way to find when the page has finished loading. My current code looks like this:
function pageLoaded(callback) {
function completed() {
document.removeEventListener('DOMContentLoaded', completed);
callback();
}
if (document.readyState === 'complete') {
callback();
} else {
document.addEventListener('DOMContentLoaded', completed);
}
}
The issue is that DOMContentLoaded
gets fired before document.readyState
gets set to 'complete' which means that the callback never gets called.
When my function runs DOMContentLoaded
has already been fired but document.readyState === 'interactive'
. However I don't think I can run my callback when document.readyState === 'interactive'
due to these possible issues.
Unfortunately I don't think I can make a demo due to the use of async. Also this doesn't happen 100% of the time but it seems to happen always when I do a hard reload (I assume something to do with caching).
Note that I'm loading in my script like this in my <head>
:
<script src="script.js" async></script>
Upvotes: 3
Views: 4600
Reputation: 5335
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:
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);
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.
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);
}
Upvotes: 6
Reputation: 84
function pageLoad(callback) {
if ("function" == typeof callback) {
if (document.addEventListener) { // Event that fires when the initial HTML document has been completely loaded and parsed
document.addEventListener("DOMContentLoaded", callback, false);
} else if (window.attachEvent) { // For IE 8 and below
window.attachEvent("onload", callback);
} else if ("function" == typeof window.onload) { // Event that fires when the page has fully loaded including images / scripts etc
var o = window.onload;
window.onload = function() {
o();
callback();
};
} else {
window.onload = callback;
}
}
}
Upvotes: 0