You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确监听window load事件并等待页面资源加载完成?

如何替换document.write并正确等待页面资源(图片等)加载完成?

我有一段完整的HTML字符串(包含<html>至</html>标签),需要用它生成页面,且必须等所有资源(尤其是图片)加载完成后,再执行后续代码。但尝试多种方法都存在问题:

  1. 直接调用resolve()导致资源未加载就继续执行:
let writeHTMLAndLoad = function() {
    return new Promise((resolve) => {
        document.open();
        window.addEventListener("load", resolve());
        document.write(strHTML);
        document.close();
    });
}
await writeHTMLAndLoad();
// 后续代码直接运行,图片等资源完全未加载
  1. 绑定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状态
  1. 强制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"
  1. 改用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 06:23:12