DOMContentLoaded 与 Load 事件的区别
1. 核心定义
(1) DOMContentLoaded
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。
触发时机:DOM 树构建完毕。
关键点:此时 JS 可以安全地访问和操作所有 DOM 节点。
(2) Load (window.onload)
当所有资源(HTML、DOM、CSS、图片、脚本、视频、iframe 等)都加载完成时,load 事件被触发。
触发时机:页面完全彻底加载完毕。
2. 详细执行流程
- 浏览器请求 HTML 文档。
- 解析 HTML,构建 DOM 树。
- 遇到
<script>(无 async/defer):暂停解析,下载并执行 JS。
- 遇到 CSS:异步下载,不阻塞 DOM 构建(但会阻塞渲染)。
- HTML 解析完毕 -> 触发
DOMContentLoaded。
- 继续加载剩余的外部资源(图片、视频、iframe)。
- 所有资源加载完毕 -> 触发
load。
3. 面试场景分析
Q: 为什么脚本通常放在<body> 底部?
为了不阻塞 HTML 的解析,让 DOM 尽快构建,使用户能更快看到页面内容。这也间接让 DOMContentLoaded 更早触发。
Q: CSS 加载会阻塞DOMContentLoaded 吗?
理论上不会,因为 DOMContentLoaded 只关注 HTML 解析。
实际上可能会:如果 JS 脚本位于 CSS 之后,浏览器为了保证 JS 能获取到正确的样式(有可能 JS 会读取 CSS),会等待 CSS 加载完毕再执行 JS。而 JS 又阻塞了 DOM 解析。此时 CSS 变相阻塞了 DOMContentLoaded。
Q: 哪个更适合性能监控?
- DOMContentLoaded: 衡量用户何时可以开始交互(First Interactive 的近似值)。
- Load: 衡量页面何时完全就绪。对于图片很多的页面,Load 时间可能很长,但用户早就开始看了。
4. 代码示例
document.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM 完全解析和加载');
});
window.addEventListener('load', (event) => {
console.log('页面所有资源加载完毕');
});
实战技巧:在开发第三方库或插件时,为了确保安全操作 DOM,通常会判断 document.readyState。如果是 interactive 或 complete,直接执行;否则监听 DOMContentLoaded。
// jQuery 的 ready 实现原理简写
function ready(fn) {
if (document.readyState !== 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}