标准答案

  1. DOMContentLoaded 在 HTML 文档解析完成、DOM 树构建完成后触发,不需要等待图片、iframe、样式背景图等外部资源全部加载完成。
  2. load 事件会等页面和依赖资源加载完成后触发,通常比 DOMContentLoaded 更晚。需要读取图片真实尺寸或等待所有资源时才更关注 load。
  3. 同步 script 放在 head 里会阻塞 HTML 解析,脚本执行时可能访问不到后面的 DOM,也可能拖慢首屏。
  4. script 放在 body 底部可以让前面的 DOM 先解析出来。现代项目更常用 defer 或 type="module",让脚本下载不阻塞解析,并在 DOM 解析完成后按顺序执行。async 下载完成后会尽快执行,不保证顺序。

题目解析

选择事件时机要看代码需要什么资源。如果只需要操作 DOM 节点,DOMContentLoaded 或 defer 通常足够;如果依赖图片、iframe 等资源加载结果,再考虑 load。

defer 脚本会在文档解析完成后、DOMContentLoaded 前执行,并保持多个 defer 脚本的顺序。type="module" 默认具有类似 defer 的行为。

async 适合不依赖 DOM 顺序、不依赖其它脚本顺序的独立脚本,例如部分统计脚本。业务主入口如果依赖执行顺序,通常不要随意改成 async。

代码示例

下面示例对比 DOMContentLoaded、load,以及 defer 脚本和底部脚本的常见位置。

HTML
<!doctype html>
<html lang="zh-CN">
  <head>
    <script defer src="/app.js"></script>
  </head>
  <body>
    <button id="save">保存</button>

    <script>
      document.addEventListener('DOMContentLoaded', () => {
        document.querySelector('#save').disabled = false
      })

      window.addEventListener('load', () => {
        console.log('页面资源已加载完成')
      })
    </script>
  </body>
</html>

常见误区

  • 误以为 DOMContentLoaded 要等图片全部加载。
  • 在 head 同步脚本中直接操作后面的 DOM。
  • 混淆 defer 和 async 的执行顺序。

作者信息