DOMContentLoaded触发时机

查看MDN的文档的时候提到DOMContentLoaded事件必须等待其所属script之前的样式表加载解析完成才会触发,在本地测试的时候发现,不管link放在script的前面还是后面,都会等样式加载回来后才触发DOMContentLoaded事件,是哪里姿势不对么?示例代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        document.addEventListener("DOMContentLoaded", function (event) {
            console.log("DOM fully loaded and parsed");
        });
    </script>
    <link rel="stylesheet" href="https://developers.google.com/blockly/styles/landing.css">
</body>

</html>

结果截图

clipboard.png

clipboard.png
上面是选了一个google域名的页面的样式,在不翻墙的情况下,样式load不下来,DOMContentLoaded事件回调的输出一直没有执行。
图二是把梯子搭上的时候,输出也在样式表请求回来后打印了,这里我把link放在了script标签后面,按文档的说法,应该立即出发DOMContentLoaded事件才对,求解答

阅读 324
评论 2019-08-30 提问
    2 个回答

    页面里的 scripts 可能会访问 css,如果不等待 css 的话,scripts 的执行结果可能有问题,所以,css 阻塞了 scripts ,而 scripts 又阻塞了 dom 解析,最终 css 的下载和解析延迟了 DOMContentLoaded 事件的触发

    你可以参考这里

    What if we replace our external script with an inline script? Even if the script is inlined directly into the page, the browser can't execute it until the CSSOM is constructed. In short, inlined JavaScript is also parser blocking.

    或者,我们看规范也行:

    3.If the list of scripts that will execute when the document has finished parsing is not empty, run these substeps:
    1.Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing has its "ready to be parser-executed" flag set and the parser's Document has no style sheet that is blocking scripts.

    .....

    4.If the list of scripts that will execute when the document has finished parsing is still not empty, repeat these substeps again from substep 1.
    .....
    4.Queue a task to run the following substeps:
    1.Fire an event named DOMContentLoaded at the Document object, with its bubbles attribute initialized to true.

    如果仔细阅读的话,这个逻辑实际是:

    script-blocking style sheet 会阻塞 scripts,而 scripts 阻塞了 dom 解析

    所以如果你的页面内没有 scripts,style sheet 就不会阻塞 dom 解析(因为它没有 script 可以阻塞,进而也就无法阻塞 dom),进而不会推迟 DOMContentLoaded 事件。

    那么怎么来验证这个结论呢?也是可以的:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Document</title>
        <link rel="stylesheet" href="http://localhost:8002/plain/test/less.css" />
      </head>
    
      <body>
        <!-- <script>
          document.addEventListener('DOMContentLoaded', function(event) {
            console.log('DOM fully loaded and parsed')
          })
        </script> -->
      </body>
    </html>

    我们可以在 fiddler 上拦截 http://localhost:8002/plain/test/less.css 这个脚本,过几秒之后在放开,结果如图:

    clipboard.png

    在 css 还没加载好的时候,DOMContentLoaded 就触发了,因为没有 scripts 受 css 阻塞,css 也就无法阻塞 dom 解析.

    如果你把脚本加上,结果就是这样:

    clipboard.png

    DOMContentLoaded 会一直等到 css 解析完成,scripts 执行完毕。因为 css 阻塞了 scripts,scripts 阻塞了 dom。

    不过,这里还有一种特殊情况

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Document</title>
      </head>
    
      <body>
        <link rel="stylesheet" href="http://localhost:8002/plain/test/less.css" />
        <!-- <script>
          document.addEventListener('DOMContentLoaded', function(event) {
            console.log('DOM fully loaded and parsed')
          })
        </script> -->
      </body>
    </html>

    如果你把 link 放在 body 里,即使没有 scripts,它也会阻塞 DOMContentLoaded 事件的触发

    评论 赞赏 2019-09-01
      Yooloo
      • 778

      你这学的什么歪门邪道,这个事件不应该注册到window上吗!

      该答案已被忽略,原因:无意义的内容 - 赞、顶、同问等毫无意义的内容

      评论 赞赏 2019-08-30
        撰写回答

        登录后参与交流、获取后续更新提醒