如何跳过未启动下载的loading="lazy"图片,完成页面图片加载等待?
如何跳过未开始下载的
loading="lazy"图片? 你的问题是原有等待图片加载的代码,遇到设置了loading="lazy"的隐藏图片时,load事件永远不会触发,导致流程卡住。要解决这个问题,核心是识别出那些浏览器还没开始下载的懒加载图片,直接跳过等待它们的加载事件。
下面提供两种可行的解决方案:
方案一:简单判断(同步逻辑)
通过图片的naturalWidth属性快速判断是否未开始下载——浏览器未启动下载的图片,naturalWidth会保持为0。结合loading属性和complete属性,就能筛选出需要跳过的懒加载图片。
修改后的代码如下:
const waitForImages = async () => { const cleanupHandler = new Set(); const images = document.querySelectorAll("img"); console.log("等待图片加载,总数:", images.length); // 判断是否是未开始下载的懒加载图片 const isUnloadedLazyImage = (image) => { return image.loading === "lazy" && !image.complete && image.naturalWidth === 0; }; await Promise.all( Array.from(images).map((image) => new Promise<void>((resolve) => { // 跳过未开始下载的懒加载图片 if (isUnloadedLazyImage(image)) { console.log(image, "跳过未启动下载的懒加载图片"); return resolve(); } if (image.complete) { return resolve(); } const timeout = setTimeout(() => { console.log(image, "加载超时"); resolve(); }, 500); const cleanup = () => { clearTimeout(timeout); image.removeEventListener("load", imageLoadingDone); image.removeEventListener("error", imageLoadingDone); cleanupHandler.delete(cleanup); }; cleanupHandler.add(cleanup); const imageLoadingDone = () => { console.log(image, "加载完成"); cleanup(); resolve(); }; image.addEventListener("load", imageLoadingDone); image.addEventListener("error", imageLoadingDone); }) ) ); console.log("所有需等待的图片已加载完成"); cleanupHandler.forEach((cleanup) => cleanup()); };
这种方法的优点是代码简单、同步执行无额外异步开销;缺点是存在极小概率的误判——比如图片已开始下载,但还没获取到元数据时,naturalWidth仍为0,可能会被错误跳过。
方案二:精准判断(异步逻辑)
利用IntersectionObserver API判断懒加载图片是否在视口内——浏览器只会加载进入视口的懒加载图片,不在视口内的图片不会触发下载,直接跳过即可。
修改后的代码如下:
const waitForImages = async () => { const cleanupHandler = new Set(); const images = document.querySelectorAll("img"); console.log("等待图片加载,总数:", images.length); // 异步判断懒加载图片是否在视口外(未开始下载) const isUnloadedLazyImage = (image) => { return new Promise((resolve) => { // 非懒加载或已加载完成的图片,直接返回false if (image.loading !== "lazy" || image.complete) { resolve(false); return; } // 用IntersectionObserver判断是否在视口内 const observer = new IntersectionObserver((entries) => { resolve(entries[0].intersectionRatio === 0); observer.disconnect(); }); observer.observe(image); }); }; await Promise.all( Array.from(images).map(async (image) => new Promise<void>(async (resolve) => { // 跳过视口外的未下载懒加载图片 if (await isUnloadedLazyImage(image)) { console.log(image, "跳过视口外的懒加载图片"); return resolve(); } if (image.complete) { return resolve(); } const timeout = setTimeout(() => { console.log(image, "加载超时"); resolve(); }, 500); const cleanup = () => { clearTimeout(timeout); image.removeEventListener("load", imageLoadingDone); image.removeEventListener("error", imageLoadingDone); cleanupHandler.delete(cleanup); }; cleanupHandler.add(cleanup); const imageLoadingDone = () => { console.log(image, "加载完成"); cleanup(); resolve(); }; image.addEventListener("load", imageLoadingDone); image.addEventListener("error", imageLoadingDone); }) ) ); console.log("所有需等待的图片已加载完成"); cleanupHandler.forEach((cleanup) => cleanup()); };
这种方法的优点是判断精准,不会出现误判;缺点是需要异步执行,代码复杂度略高。
内容的提问来源于stack exchange,提问作者jantimon
相关产品推荐
相关产品推荐

