如何在页面(含外部资源)加载完成前显示加载动画?
页面加载动画未检测全部资源的问题排查与解决
我希望在页面**完全加载(包括外部广告横幅等资源)**前显示加载动画,目前用以下代码实现加载动画的显示与过渡隐藏,但怀疑当前逻辑没有检测到所有页面资源:
HTML代码
<div class="loader"></div>
CSS代码
.loader { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; background: #333333; transition: opacity 0.75s, visibility 0.75s; } .loader--hidden { opacity: 0; visibility: hidden; } .loader::after { content: ""; width: 75px; height: 75px; border: 15px solid #dddddd; border-top-color: #009578; border-radius: 50%; animation: loading 0.75s ease infinite; } @keyframes loading { from { transform: rotate(0turn); } to { transform: rotate(1turn); } }
JS代码
window.addEventListener("load", () => { const loader = document.querySelector(".loader"); loader.classList.add("loader--hidden"); loader.addEventListener("transitionend", () => { document.body.removeChild(loader); }); });
我参考了相关代码实现,但目前的逻辑可能存在资源检测不全的问题。
问题分析
window.load事件确实会等待页面初始资源(图片、样式表、脚本、iframe等)加载完成后触发,但广告横幅这类由第三方脚本动态加载的资源,往往不会被window.load覆盖——因为这类广告通常是页面初始资源加载完成后,由JS动态创建元素并发起请求,这时候window.load已经触发,导致加载动画提前隐藏,广告还在加载。
解决方案
1. 追踪广告资源加载状态
如果广告平台提供加载完成的回调(比如多数平台会有onAdLoaded类事件),可以同时监听页面load事件和广告加载回调,等两者都完成后再隐藏加载动画:
let pageLoaded = false; let adLoaded = false; // 监听页面初始资源加载完成 window.addEventListener("load", () => { pageLoaded = true; checkAllLoaded(); }); // 监听广告加载完成(需根据广告平台实际事件名调整) window.addEventListener("adLoaded", () => { adLoaded = true; checkAllLoaded(); }); // 统一检查所有资源加载状态 function checkAllLoaded() { if (pageLoaded && adLoaded) { const loader = document.querySelector(".loader"); loader.classList.add("loader--hidden"); loader.addEventListener("transitionend", (e) => { // 避免transitionend多次触发 if (e.propertyName === "opacity") { document.body.removeChild(loader); } }); } }
2. 兜底方案:动态检测DOM变化
如果广告平台没有提供加载回调,可以通过监听广告容器的DOM变化,结合定时器兜底:
window.addEventListener("load", () => { const loader = document.querySelector(".loader"); const adContainer = document.querySelector("#ad-container"); // 替换为你的广告容器选择器 // 监听广告容器的DOM节点变化 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { // 根据广告元素特征判断是否加载完成(示例:检测iframe或带ad类名的元素) const hasAd = Array.from(mutation.addedNodes).some(node => node.tagName === "IFRAME" || node.classList.contains("ad-banner") ); if (hasAd) { // 给广告内容预留加载时间 setTimeout(() => { hideLoader(loader); observer.disconnect(); }, 500); } } }); }); observer.observe(adContainer, { childList: true, subtree: true }); // 兜底定时器:避免广告加载异常导致动画一直显示 setTimeout(() => { hideLoader(loader); observer.disconnect(); }, 5000); // 5秒后强制隐藏 }); function hideLoader(loader) { loader.classList.add("loader--hidden"); loader.addEventListener("transitionend", (e) => { if (e.propertyName === "opacity") { document.body.removeChild(loader); } }); }
3. 修复过渡事件重复触发问题
当前代码中transitionend会因opacity和visibility两个属性的过渡各触发一次,可通过判断属性名只执行一次移除操作:
loader.addEventListener("transitionend", (e) => { if (e.propertyName === "opacity") { document.body.removeChild(loader); } });
内容的提问来源于stack exchange,提问作者Mohamad Ahmad
相关产品推荐
相关产品推荐

