应使用 document.addeventlistener('domcontentloaded', ...),因其在 html 解析完成、dom 树建好后立即触发,不依赖 css、图片等资源加载;而 onload 仅适用于 body、img 等少数元素,且需等待所有资源加载完毕,易导致延迟或失效。

用 document.addEventListener('DOMContentLoaded', ...),不是 onload,也不是把脚本塞在 底部碰运气。
为什么不能用 onload 属性写在任意标签上
比如 <p onload="init()"></p> 这种写法浏览器直接忽略——onload 不是 <p></p> 的合法属性。HTML 规范只允许它出现在 、<img>、<iframe></iframe>、<script></script> 等少数元素上。强行加只会让代码看起来像在运行,实际什么也没触发。
常见错误现象:
- 控制台没报错,但函数完全不执行
- 页面刷新后偶尔生效、偶尔失效(其实是 DOM 尚未就绪时脚本已执行)
DOMContentLoaded 和 window.onload 到底差在哪
关键区别不在“谁先谁后”,而在“等不等资源”:
-
DOMContentLoaded:HTML 解析完、DOM 树建好就触发,不管 CSS、图片、字体有没有加载完 -
window.onload:所有资源(含图片、样式表、子 iframe)全部下载并解析完成才触发,通常晚几百毫秒甚至几秒
所以:
- 要绑定按钮点击、读取
document.body、初始化菜单——用DOMContentLoaded - 要获取图片真实尺寸、依赖 CSSOM 计算布局(比如
getBoundingClientRect())——得等window.onload或手动监听document.fonts.load等 - 如果用了
link[rel=stylesheet]且没加media="print"等惰性加载策略,DOMContentLoaded触发时 CSSOM 可能还没就绪,getComputedStyle()返回空值或默认值
监听 DOMContentLoaded 的正确写法和坑点
最稳妥的写法就是直接监听 document:
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 已就绪');
const el = document.getElementById('main');
if (el) el.classList.add('loaded');
});
注意事项:
- 这段代码可以放在
里,也可以放在任何位置,浏览器会缓存事件并等 DOM 就绪后派发 - 不要用
document.onreadystatechange+readyState === 'interactive'来模拟,行为不一致且 IE 兼容性差 - 别在回调里反复调用
document.getElementById却不判空——元素可能真不存在,得自己加if - 事件只触发一次,不需要手动
removeEventListener,除非你主动注册了多次且想中途取消
脚本位置和执行时机的真实关系
很多人以为把 <script></script> 放在 前面就“一定安全”,其实有例外:
- 如果前面有同步加载的外部脚本(比如没加
async或defer的<script src="a.js"></script>),而它内部又执行了耗时操作(如大循环、同步 XHR),那后面的内联脚本仍可能在 DOM 就绪前执行 -
DOMContentLoaded是浏览器级信号,比“脚本位置”更可靠;它确保 HTML 解析器已完成工作,无论脚本怎么放 - 现代项目中,如果用了模块化打包(如 Vite、Webpack),入口脚本默认就是等 DOM 就绪后才执行,本质仍是封装了
DOMContentLoaded逻辑
真正容易被忽略的是 CSS 阻塞:哪怕 DOM 已就绪,只要关键样式表还没解析完,offsetHeight、clientWidth 这类布局 API 就可能返回 0 或过时值——这不是事件监听的问题,而是渲染流水线本身的依赖关系。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











