DOMContentLoaded触发时机

探究DOMContentLoaded事件的触发时机

MDN上的解释是:当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。注意:DOMContentLoaded 事件必须等待其所属script之前的样式表加载解析完成才会触发。这两句话看似有点矛盾,我们看下HTML5规范是怎么解释这个事情的。

html5规范

the_end

  • 规范上写的是,一旦客户端(也就是浏览器)停止解析文档时,如果has no style sheet that is blocking scripts(没有阻塞脚本的style sheet),就会在执行完list of scripts that will execute when the document has finished parsing里的所有脚本后,触发DOMContentLoaded 事件。

style sheet that is blocking scripts

html规范中有解释这个是什么
style

  • has no style sheet that is blocking scripts 其实就是说Documentscript-blocking style sheet counter 为 0。而script-blocking style sheet counter在解析一个link标签是+1,load完一个就-1
  • 也就是说一个正常的link标签都会是一个block scripts的style sheet。是我理解错了?因为实际情况是css文件不会阻塞DOMContentLoaded事件的触发。
  • 看这个demo,注意把网速调低,可以看到控制台内的DOMContentLoaded的输出不会等待css加载完。
  • css在什么情况才会阻塞DOMContentLoaded事件的触发呢?css后面有同步脚本的时候,因为css会阻塞后面js的执行(不会阻塞加载),而同步脚本的执行会阻塞DOMContentLoaded事件的触发,可以看这个demo
  • 因为上面的原因,在绑定DOMContentLoaded事件的js代码前的css会阻塞事件的触发

list of scripts that will execute when the document has finished parsing

html规范中有解释这个
scripting
也就是说只要满足以下任意一个条件,就会被加入到需要再完成dom解析后执行的脚本队列

  • 普通脚本(type=”text/javascript”),有src属性,并且有defer属性,没有async属性
  • module脚本(type=”module”),并且没有async属性

也就是说以上形式的脚本会阻塞DOMContentLoaded事件的触发

结论

  • DOMContentLoaded 事件会在dom解析完、同步js执行完后、所有带有defer属性的脚本、type=”module”的脚本执行后再触发。
  • 正常情况下css不会阻塞DOMContentLoaded 事件的触发,但是有可能会因为css阻塞js的执行,从而阻塞DOMContentLoaded 事件。

参考文章