簡體   English   中英

等到頁面上的所有圖像都加載完畢

[英]Wait until all images on the page have been loaded

我想等到沒有更多圖片下載:

const waitForImages = async () => {
  const cleanupHandler = new Set();
  const images = document.querySelectorAll("img");
  console.log("wait for images", images.length);
  await Promise.all(
    Array.from(images).map((image) =>
      new Promise<void>((resolve) => {
        if (image.complete) {
          return resolve();
        }

        const timeout = setTimeout(() => {
          console.log(image, "timed out");
          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, "done");
          cleanup();
          resolve();
        };
        image.addEventListener("load", imageLoadingDone);
        image.addEventListener("error", imageLoadingDone);
      })
    )
  );
  console.log("all images loaded");
  cleanupHandler.forEach((cleanup) => cleanup());
}

該代碼運行良好。 不幸的是,一旦頁面上有一個隱藏的<img loading="lazy"> load事件就永遠不會被觸發。 (參見HTML 圖片元素加載

有什么辦法可以跳過那些沒有開始下載的loading="lazy"圖像嗎?

嘗試這個

Array.from(images).filter((image) => image.loading !== "lazy").map(...)

我找到了一個解決方案,但我仍然希望有一個不那么復雜的解決方案:

/**
 * Wait until all images are downloaded and encoded.
 */
export const waitForImages = async () => {
  // Get only images in view - this is a Workaround for lazy loading images
  const images = Array.from(await getAllImagesIntersectingViewport());
  const imageLoadPromisses = Array.from(images).map(
    (image) =>
      new Promise<void>((resolve) => {
        if (image.complete) {
          return resolve();
        }
        // fallback timeout to prevent freezing the entire test
        const timeout = setTimeout(() => {
          console.log("timeout", image);
          resolve();
        }, 2000);
        const cleanup = () => {
          clearTimeout(timeout);
          image.removeEventListener("load", imageLoadingDone);
          image.removeEventListener("error", imageLoadingDone);
        };
        const imageLoadingDone = () => {
          cleanup();
          image
            .decode()
            .catch(() => {
              /* ignore decoding erros */
            })
            .then(resolve);
        };
        image.addEventListener("load", imageLoadingDone);
        image.addEventListener("error", imageLoadingDone);
      })
  );
  // wait until all images are loaded
  await Promise.all(imageLoadPromisses);
  return images;
};

/**
 * Get all images that are currently inside the viewport
 */
const getAllImagesIntersectingViewport = async () => {
  const intersectingImages = new Set<HTMLImageElement>();
  const images = document.querySelectorAll("img");
  if (images.length) {
    let observer: IntersectionObserver | undefined;
    await new Promise<void>((resolve) => {
      const imageObserver = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (
            entry.isIntersecting &&
            entry.target instanceof HTMLImageElement
          ) {
            intersectingImages.add(entry.target);
          }
        });
        resolve();
      });
      observer = imageObserver;
      images.forEach((image) => imageObserver.observe(image));
    });
    if (observer) {
      observer.disconnect();
    }
  }
  return intersectingImages;
};

這個想法太簡單了,以至於我可能誤解了問題(如果是的話,請道歉)。

這個想法是通過直接將 function 中的"img"選擇器替換為以下內容,將查詢選擇器限制為那些缺少loading="lazy"屬性的圖像:

"img:not([loading='lazy'])"

以下代碼段包含七個圖像元素,其中四個具有loading="lazy"屬性。 使用上述選擇器引用的 html 集合包含 3 個項目,對應於 3 個“正常”圖像元素。

 const images = document.querySelectorAll("img:not([loading='lazy'])"); console.log(images.length);
 <img src="path/1.png" loading="lazy" /> <img src="path/2.png" /> <img src="path/3.png" loading="lazy" /> <img src="path/4.png" /> <img src="path/5.png" /> <img src="path/6.png" loading="lazy" /> <img src="path/7.png" loading="lazy" />

這里抓取

我們將在幕后克隆每個圖像(即不將其渲染到 DOM),然后監聽它的加載。 新腳本可能如下所示:

$(document).ready(function () {
  // Images loaded is zero because we're going to process a new set of images.
  var imagesLoaded = 0;
  // Total images is still the total number of <img> elements on the page.
  var totalImages = $("img").length;

  // Step through each image in the DOM, clone it, attach an onload event
  // listener, then set its source to the source of the original image. When
  // that new image has loaded, fire the imageLoaded() callback.
  $("img").each(function (idx, img) {
    $("<img>").on("load", imageLoaded).attr("src", $(img).attr("src"));
  });

  // Do exactly as we had before -- increment the loaded count and if all are
  // loaded, call the allImagesLoaded() function.
  function imageLoaded() {
    imagesLoaded++;
    if (imagesLoaded == totalImages) {
      allImagesLoaded();
    }
  }

  function allImagesLoaded() {
    console.log("ALL IMAGES LOADED");
  }
});

如果只想跳過它們,您可以通過加載屬性來簡單地過濾圖像

const images = document.querySelectorAll("img").filter(img => img.loading !== "lazy");

這樣你的代碼就不會等待惰性圖像。 如果您想要下載所有圖像,您可以通過編程方式將加載設置為“自動”,但我相信這不是你的情況

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM