设置Slow 3G时MutationObserver未触发的技术问题排查
问题背景
我希望在动态创建的Hero Image加载完成后,再延迟加载非必要脚本。
代码实现
"use strict"; import { siteNavHandler } from "./siteNavHandler.min.js"; import { buildHeroGallery } from "./buildHeroGallery2.min.js"; import { pageNavHandlerList } from "./pageNavList.min.js"; import { lazyLoadingCtaSection } from "./lazyLoadingCtaSection.min.js"; document.addEventListener("DOMContentLoaded", init, false); function init() { siteNavHandler(); DelayScriptLoading(); buildHeroGallery(); // 该模块负责加载动态Hero Image pageNavHandlerList(); lazyLoadingCtaSection(); } function DelayScriptLoading() { const interval = setInterval(function () { if (document.querySelector("#heroImage0")) { console.log("Found! with polling"); const img = document.querySelector("#heroImage0"); img.addEventListener("onload", loadjs("polling event listener on load", img)); clearInterval(interval); } }, 500); const target = document.getElementById("top"); console.dir(target); function addHandler(records, observer) { for (const record of records) { for (const addedNode of record.addedNodes) { if (addedNode.id == "heroImage0") { console.log("MutationObserver triggered for " + `${addedNode.id}`); const img = addedNode.querySelector(".hero-gallery-img"); img.addEventListener("onload", loadjs("MutationObserver event listener on load", img)); } observer.disconnect(); } } } const observerOptions = { childList: true, subtree: true, }; const observer = new MutationObserver(addHandler); observer.observe(target, observerOptions); async function loadjs(param, img) { console.log("img has loaded and load JS function triggered using " + `${param}`); console.dir(img); } }
问题现象
将Chrome浏览器开发者工具的网络节流设置为Slow 3G时,MutationObserver未触发,但轮询机制可正常触发;设置为Fast 3G或更高速率时,两者均能正常触发。
控制台输出
- Slow 3G下:
controllerList.js:28 section#top.heroGalleryContainer controllerList.js:20 Found! with polling controllerList.js:52 img has loaded and load JS function triggered using polling event listener on load controllerList.js:53 img#heroImage0.hero-gallery-img
- Fast 3G及以上:
controllerList.js:28 section#top.heroGalleryContainer controllerList.js:34 MutationObserver triggered for heroImage0 controllerList.js:52 img has loaded and load JS function triggered using MutationObserver event listener on load controllerList.js:53 img.hero-gallery-img controllerList.js:20 Found! with polling controllerList.js:52 img has loaded and load JS function triggered using polling event listener on load controllerList.js:53 div#heroImage0.hero-gallery-div.img--FadeIn
补充说明
使用script defer无法满足需求,因为需要脚本在动态Hero Image加载完成后再加载;async加载也不可行,因为不希望脚本在后台占用带宽,直到Hero Image加载完成。
核心问题
为什么Slow 3G下MutationObserver不触发?
原因分析与修复
根本原因
问题出在addHandler函数里的observer.disconnect()调用时机错误:
在Slow 3G环境下,buildHeroGallery创建DOM节点的过程可能是分批异步完成的,当第一个非heroImage0的节点被添加到#top容器时,循环会直接执行observer.disconnect(),导致Observer提前停止监听,后续真正的heroImage0节点添加时无法触发回调。
而在Fast 3G或更快的网络下,heroImage0可能是第一批被添加的节点,Observer在处理完目标节点后才断开,因此能正常工作。
附带问题
- 事件绑定错误:
img.addEventListener("onload", loadjs(...))直接调用了loadjs,应该传递函数引用,且正确事件名是load而非onload。 - DOM选择容错:如果
heroImage0本身就是img元素,addedNode.querySelector(".hero-gallery-img")会找不到元素。
修复代码
调整Observer断开时机,并修复事件绑定问题:
function addHandler(records, observer) { for (const record of records) { for (const addedNode of record.addedNodes) { if (addedNode.id == "heroImage0") { console.log("MutationObserver triggered for " + `${addedNode.id}`); // 兼容目标节点本身是img的情况 const img = addedNode.querySelector(".hero-gallery-img") || addedNode; // 传递函数引用而非直接执行 img.addEventListener("load", () => loadjs("MutationObserver event listener on load", img)); // 找到目标节点后再断开监听 observer.disconnect(); return; // 退出循环避免多余操作 } } } }
同时修正轮询逻辑里的事件绑定:
const img = document.querySelector("#heroImage0"); img.addEventListener("load", () => loadjs("polling event listener on load", img));
内容的提问来源于stack exchange,提问作者EL_EL
相关产品推荐
相关产品推荐

