如何正确监听window load事件并等待页面资源加载完成?
如何替换document.write并正确等待页面资源(图片等)加载完成?
我有一段完整的HTML字符串(包含<html>至</html>标签),需要用它生成页面,且必须等所有资源(尤其是图片)加载完成后,再执行后续代码。但尝试多种方法都存在问题:
- 直接调用
resolve()导致资源未加载就继续执行:
let writeHTMLAndLoad = function() { return new Promise((resolve) => { document.open(); window.addEventListener("load", resolve()); document.write(strHTML); document.close(); }); } await writeHTMLAndLoad(); // 后续代码直接运行,图片等资源完全未加载
- 绑定
resolve到load事件却陷入永久等待:
let writeHTMLAndLoad = function() { return new Promise((resolve) => { function dummy() { resolve(); } document.open(); window.addEventListener("load", dummy); document.write(strHTML); document.close(); }); } await writeHTMLAndLoad(); // Promise永远处于pending状态
- 强制resolve后发现
load事件实际会触发,但时机太晚:
let writeHTMLAndLoad = function() { return new Promise((resolve) => { function dummy() { console.log("Loaded"); } document.open(); window.addEventListener("load", dummy()); document.write(strHTML); document.close(); resolve(); }); } await writeHTMLAndLoad(); // 后续代码执行过程中才打印"Loaded"
- 改用DOM操作替换
innerHTML,依然无法等待资源加载:
let tempDoc = new DOMParser().parseFromString(strHTML, "text/html"); document.documentElement.innerHTML = tempDoc.documentElement.innerHTML;
我需要一种既能替代document.write,又能通过Promise连贯等待资源加载完成的方案。
核心原因
window的load事件仅在页面首次加载完成时触发一次。当你通过document.open()重写HTML或替换documentElement内容后,该事件不会再次触发——这就是之前代码永久等待的根本原因。因此不能依赖window.load,必须手动跟踪资源加载状态。
正确实现方案
1. 安全替换document.write的方式
用DOMParser解析HTML字符串后,直接替换整个文档的根节点,避免innerHTML可能带来的解析问题:
const tempDoc = new DOMParser().parseFromString(strHTML, "text/html"); document.replaceChild(tempDoc.documentElement, document.documentElement);
2. 等待所有资源加载的Promise封装
收集新HTML中的所有资源(图片、样式表、外部脚本),监听它们的load和error事件,全部完成后再resolve:
function loadHTMLAndWaitForResources(htmlStr) { return new Promise((resolve) => { const tempDoc = new DOMParser().parseFromString(htmlStr, "text/html"); const newRoot = tempDoc.documentElement; // 筛选需要等待的资源类型 const resources = [ ...newRoot.querySelectorAll("img"), ...newRoot.querySelectorAll("link[rel='stylesheet']"), ...newRoot.querySelectorAll("script[src]") ]; // 无资源时直接替换并resolve if (resources.length === 0) { document.replaceChild(newRoot, document.documentElement); resolve(); return; } let completedCount = 0; const total = resources.length; // 资源完成(加载成功/失败)的统一回调 function markCompleted() { completedCount++; if (completedCount === total) { resolve(); } } // 给每个资源绑定事件 resources.forEach(res => { res.addEventListener("load", markCompleted); res.addEventListener("error", markCompleted); // 失败也标记完成,避免Promise挂起 }); // 插入新文档,触发资源加载 document.replaceChild(newRoot, document.documentElement); }); }
3. 使用示例
await loadHTMLAndWaitForResources(strHTML); // 后续代码:此时所有图片、样式表、外部脚本均已加载完成
额外说明
- 处理
error事件是为了避免单个资源加载失败导致Promise永久pending,你可以根据需求在markCompleted中添加错误日志或提示逻辑。 - 若仅需等待图片,可移除样式表和脚本的筛选,只保留
img元素。 - 此方式符合现代DOM操作规范,比
document.write更安全,不会阻塞页面解析流程。
内容的提问来源于stack exchange,提问作者Jono
相关产品推荐
相关产品推荐

