Javascript:页面加载完成后运行函数,脚本加载异步

jos*_*unt 2 javascript asynchronous

我无法找到一种可靠的方法来查找页面何时完成加载.我当前的代码如下所示:

function pageLoaded(callback) {
    function completed() {
        document.removeEventListener('DOMContentLoaded', completed);
        callback();
    }

    if (document.readyState === 'complete') {
        callback();
    } else {
        document.addEventListener('DOMContentLoaded', completed);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我的函数运行时DOMContentLoaded已经被解雇但是document.readyState === 'interactive'.但是,document.readyState === 'interactive'由于这些可能的问题,我不认为我可以运行我的回调.

不幸的是,由于使用了异步,我认为我不能进行演示.这也不会100%发生,但似乎总是在我进行硬重载时发生(我假设与缓存有关).

请注意,我在我的脚本中加载如下<head>:

<script src="script.js" async></script>
Run Code Online (Sandbox Code Playgroud)

jos*_*unt 5

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

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

然后我录的状态document.ready,并DOMContentLoaded在每个阶段.我的代码:

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);
Run Code Online (Sandbox Code Playgroud)

我的测试结果

First Buffer
Chrome(v43)/ FF(v39)/ IE11: document.ready === 'loading'
IE9/IE10: document.ready === 'interactive'

最终缓冲
铬/ FF/IE11: document.ready === 'interactive',DOMContentLoaded所谓
IE9/IE10:在没有变化document.ready,DOMContentLoaded称为

子资源完成加载(在本例中为图像)
Chrome/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);
}
Run Code Online (Sandbox Code Playgroud)

2. DOMContentLoaded<head>异步脚本之外添加文档.这可确保在调用之前将其附加.

// In <head>
<script>
    var pageLoaded = false;

    document.addEventListener('DOMContentLoaded', function() {
        pageLoaded = true;
    });
</script>

// In script.js
if (pageLoaded) {
    callback();
} else  {
    document.addEventListener('DOMContentLoaded', callback);
}
Run Code Online (Sandbox Code Playgroud)

3.回到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);
}
Run Code Online (Sandbox Code Playgroud)