为什么script标签放在head中无法获取DOM元素,放在body末尾就正常运行?
问题原因解析
1 报错的根本原因
浏览器解析HTML页面的顺序是从上到下逐行执行:
- 当你把script放在
head标签末尾时,此时浏览器还没开始解析body里的内容,<video id="video">元素还没有被挂载到DOM树上,document.getElementById("video")的返回结果为null,后续尝试给null设置width属性,自然就抛出Cannot set properties of null (setting 'width')的错误。 - 当你把script放在
body标签末尾时,此时body里的所有DOM元素都已经解析完成,能正常获取到video元素,所以代码可以正常运行。
2 script放在head和body中的核心区别
两者的核心差异集中在执行时机和对页面性能的影响两个维度:
- 执行时机不同:无特殊属性的普通script标签放在
head中时,会在DOM解析、页面渲染前就加载并执行;放在body末尾的script会在所有DOM元素解析完成后才执行,不需要额外监听就能直接操作页面上的所有已生成元素。 - 对渲染的影响不同:普通script标签的加载和执行会阻塞DOM解析和页面渲染,放在
head中如果脚本体积大、执行逻辑重,会拉长用户的首屏白屏时间;放在body末尾不会阻塞首屏内容的渲染,用户感知的页面加载速度更快。
3 script标签放置位置的选择建议
不需要始终将script放在body末尾,可以根据场景判断:
- 绝大多数和DOM操作相关、不需要提前执行的业务脚本,推荐放在
body末尾,是兼容性最好、性能开销最低的方案。 - 以下场景可以将script放在
head中:- 全局基础配置类脚本,比如全局环境变量定义、前端框架的前置初始化配置
- 首屏性能统计、异常监控类需要提前启动的脚本
- 加了
async/defer异步属性的第三方工具库,异步加载不会阻塞页面渲染
如果你需要把操作DOM的脚本放在head中,可以通过监听DOMContentLoaded事件避免找不到元素的问题,示例代码如下:
<script> var video; // 等待DOM完全解析完成后再获取元素 document.addEventListener('DOMContentLoaded', function() { video = document.getElementById("video"); changeVideoSize(); // 页面加载完成先执行一次尺寸适配 }) function changeVideoSize() { if(!video) return; // 兜底判断,避免元素未初始化时执行报错 console.log("width is: ", window.innerWidth) if (window.innerWidth > 900) { video.width = "640"; video.height = "480"; } else { video.width = "320"; video.height = "240"; } } window.addEventListener("resize", changeVideoSize) </script>
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

