Javascript:当页面加载完成时运行函数,脚本正在异步加载

Javascript: Run function when page has finished loading, script is loading async

本文关键字:加载 脚本 异步 函数 完成时 Javascript 运行      更新时间:2023-09-26

我找不到可靠的方法来查找页面何时加载完成。我当前的代码如下:

function pageLoaded(callback) {
    function completed() {
        document.removeEventListener('DOMContentLoaded', completed);
        callback();
    }
    if (document.readyState === 'complete') {
        callback();
    } else {
        document.addEventListener('DOMContentLoaded', completed);
    }
}

问题是DOMContentLoadeddocument.readyState设置为"complete"之前被激发,这意味着永远不会调用回调。

当我的函数运行时,DOMContentLoaded已经被激发,但document.readyState === 'interactive'。然而,由于这些可能的问题,我认为我不能在document.readyState === 'interactive'时运行回调。

不幸的是,由于使用了异步,我认为我无法制作演示。此外,这种情况并非100%发生,但似乎总是在我进行硬重新加载时发生(我认为这与缓存有关(。

注意,我在<head>:中像这样加载脚本

<script src="script.js" async></script>

我找不到太多关于这个主题的有用信息,所以我决定做一些测试。我设置了一个节点服务器,它将:

  1. 发送html文档的开头
  2. 等待5秒
  3. 发送包含图像的html的其余部分

然后我记录了document.readyDOMContentLoaded在每个阶段的状态。我的代码:

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);

我的测试结果

第一缓冲区
Chrome(v43(/FF(v39(/IE11:document.ready === 'loading'
IE9/IE10:document.ready === 'interactive'

最终缓冲区
Chrome/FF/IE11:document.ready === 'interactive'DOMContentLoaded调用
IE9/IE10:document.readyDOMContentLoaded无变化,称为

子资源完成加载(在本例中为图像(
铬/FF/IE11:document.ready === 'complete'
IE9/IE10:document.ready === 'complete'

正如你所看到的IE9&IE10过早设置document.ready === 'interactive'

一些可能的解决方案

1.忽略IE9/IE10

if (document.readyState === 'interactive' || document.readyState === 'complete') {
    callback();
} else {
    document.addEventListener('DOMContentLoaded', callback);
}

2.在异步脚本之外的文档的<head>中添加DOMContentLoaded。这样可以确保在调用之前先附加它

// 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.回退到`window上的load事件

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);
}
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;
        }
    }
}