You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
注册

如何在页面加载时控制多脚本执行并确保DOM加载完成后脚本正确运行?

如何在页面加载时控制多脚本执行并确保DOM加载完成后脚本正确运行?

嘿,这个问题我之前刚踩过同款坑!你说的没错,jQuery的$(function(){...})确实够贴心,不管什么时候调用,DOM就绪了就直接跑回调,没就绪就乖乖等,但原生JS的DOMContentLoaded是真的只触发一次——要是你的脚本在事件触发之后才加载完成(比如你index.js里有await请求拖慢了执行时机),那绑定的监听函数就彻底没机会跑了。

给你分享几个我日常开发里用的靠谱方案:

1. 封装一个类似jQuery ready的工具函数(最推荐)

自己写个几行的小工具,完美兼容“DOM已就绪”和“未就绪”两种情况,用起来和jQuery的ready几乎一模一样:

先在所有脚本都能访问到的地方(比如公共的menu.js最开头,或者单独的公共工具脚本)定义这个函数:

function onDomReady(callback) {
  // 先检查DOM当前状态:interactive表示DOM已经解析完成,complete是所有资源都加载完毕
  if (document.readyState === 'interactive' || document.readyState === 'complete') {
    // 已经就绪,直接执行回调
    callback();
  } else {
    // 还没准备好,监听DOMContentLoaded事件
    document.addEventListener('DOMContentLoaded', callback);
  }
}

然后不管是menu.js还是index.js,都用这个函数包起DOM相关逻辑:

menu.js里:

onDomReady(() => {
  document.querySelector(".navbar-menu").addEventListener('click', () => {
    // 你的菜单点击逻辑
  });
});

index.js里(先拿数据再等DOM,逻辑更清晰):

// 先异步拿数据,这部分不依赖DOM
const { httpConstants } = (await axios.get('/some-data')).data;

// 再用工具函数处理DOM相关逻辑
onDomReady(() => {
  // 这里放页面特定的DOM操作逻辑
});

这个方法的好处是复用性拉满,不管脚本加载早或晚,甚至是异步加载的,都能正确触发回调。

2. 直接检查readyState后执行(适合简单场景)

要是不想封装函数,也可以在每个脚本的DOM逻辑前直接判断状态:

比如index.js里可以这么写:

const { httpConstants } = (await axios.get('/some-data')).data;

// 直接判断DOM是否就绪
if (document.readyState === 'interactive' || document.readyState === 'complete') {
  // 已经就绪,直接执行DOM逻辑
  // ...你的页面专属逻辑
} else {
  // 没就绪,等DOMContentLoaded事件
  document.addEventListener('DOMContentLoaded', () => {
    // ...你的页面专属逻辑
  });
}

不过这种写法会重复代码,多个脚本都这么写就很繁琐,还是封装成函数更省心。

3. 利用ES模块的默认行为(针对你的index.js)

你要知道,浏览器加载type="module"的脚本时,默认是延迟执行的——也就是会等DOM解析完成后才执行模块的顶层代码。但你index.js里的await axios.get(...)会让模块执行暂停,直到请求完成,这时候DOMContentLoaded可能已经触发完了,所以你后来加的addEventListener就监听不到了。

如果你的异步请求不需要在DOM就绪前做,其实可以把await放到DOM就绪的回调里:

onDomReady(async () => {
  // 等DOM就绪了再拿数据,避免错过事件
  const { httpConstants } = (await axios.get('/some-data')).data;
  // ...你的页面逻辑
});

这样就不会因为异步请求拖慢,导致错过DOMContentLoaded事件了。

最后再给你提个醒:绝对不要手动触发DOMContentLoaded事件!你之前试的那个方法会触发递归错误,只要触发这个事件,所有监听它的回调都会执行,要是某个回调里又不小心触发了(或者有其他逻辑间接触发),直接就无限循环炸锅😂。

火山引擎 最新活动