DOMContentLoaded
什么是DOMContentLoaded事件?
首先想到的是查看W3C的HTML5规范,DOMContentLoaded事件在什么时候触发:
当用浏览器停止解析文本时,它会运行以下几个步骤:
1. 设置当前的文本的状态为“interactive”,插入点”undefined”。
2. 将“open elements”堆栈里面所有元素清空。
3. 如果延迟加载的脚本列表不为空,执行下面的步骤:
- 延长事件循环直到延迟加载脚本列表里面第一个脚本被表示为“ready to be parser-executed”并且解析器被没有样式阻塞。
- 执行延迟加载脚本列表里面的第一个脚本。
- 从延迟加载脚本列表移除脚本元素。(i.e: 移除列表的第一个条目)
- 如果脚本列表还是不为空,重复上述步骤。
4.触发DOMContentLoaded事件
再看看MDN上有关DOMContentLoaded事件的文档:
当文本完全加载并解析完时,触发DOMContentLoaded事件,不会等待样式,图片或者iframe去加载完成。
注:样式加载会阻塞脚本执行,如果 <script>
标签放在<link>
后面,脚本会一直等到样式加载完才继续解析,DOMContentLoaded也不会触发。
实验1:无脚本时,DOMContentLoaded事件不需等待css文件、图片加载完成
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" type="text/css" href="./css/main.css">
</head>
<body>
<p>Content</p>
<img src="./img/chrome-girl.jpg">
</body>
</html>
图1
实验2:DOMContentLoaded事件需要等待JS执行完才触发
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
console.timeStamp('Inline script before link in head');
window.addEventListener('DOMContentLoaded', function(){
console.timeStamp('DOMContentLoaded event');
});
</script>
<link rel="stylesheet" type="text/css" href="../css/styles.css">
<script type="text/javascript">
console.timeStamp('Inline script after link in head');
</script>
</head>
<body>
<img src="../img/github.jpg">
<script type="text/javascript" src="./js/main.js"></script>
</body>
</html>
图二
如果页面中静态的写有script标签,DOMContentLoaded事件需要等待JS执行完才触发。
而script标签中的JS需要等待位于其前面的CSS的加载完成。
图三
可以看到,浏览器会并发的预加载CSS,IMG, JS,也就是第一次JS阻塞时就并发的请求这些资源。但是,执行CSS和JS的顺序还是按原来的依赖顺序(JS的执行要等待位于其前面的CSS和JS加载、执行完)。先加载完成的资源,如果其依赖还没加载、执行完,就只能等着。
一些优化网页性能的建议:
- 减小CSS文件体积,把单个CSS文件分成几个文件以并行加载,减少CSS对JS的阻塞时间
- 次要的JS文件,通过动态插入script标签来加载(动态插入的script标签不阻塞DOMContentLoaded事件的触发)
- CSS中使用的sprites图,可以利用对img的预加载,放在html中跟CSS文件一起加载