該如何知道圖片載入完成沒?

前言

有時候會有一些狀況會是需要判斷當前頁面圖片載入完畢沒有,因此這一篇就來紀錄一下如何處理吧。

Image

圖片在網頁開發上是非常常見的元素,舉凡產品列表這區塊就非常的常見,畢竟你一定會上傳圖片,否則你要如何讓使用者知道產品樣子呢?因此我們就會使用到 new Image()

而依照 MDN 說明 new Image() 等同於 document.createElement('img'),因此在 MDN 上就有一段範例是關於 new Image() 寫法

1
2
3
var myImage = new Image(100, 200);
myImage.src = 'picture.jpg';
document.body.appendChild(myImage);

當然這種寫法是屬於 JavaScript 動態寫入圖片的方法,因此我們會需要使用 new Image() 的特性來了解圖片載入狀況。

為了講點實例,所以下方我一次載入了七張圖片

1
2
3
4
5
6
7
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">

接下來透過 JS 來判斷他是否圖片已經載入完畢

1
2
3
4
5
6
7
const imgID = document.querySelectorAll('.img');

imgID.forEach((item, index) => {
const img = new Image();
img.src = item.src;
img.addEventListener("load", () => console.log(`圖片載入完成${index}`));
})

透過這個方式我們會看到 7 次的 console.log 這種方式可以確保每一張圖片載入才出現在畫面,基本上你會發現每一張圖載入的順序都不同,只是因為我圖片太小所以載入都很快,但透過 index 順序可以了解到每一張圖載入速度都不同。

非順序的圖片

而這邊在我們使用 JavaScipt 撈出圖片的 Url 之後會寫入到 new Image(); 出來的 src,路徑,此時我們就可以去監聽圖片載入的事件,透過這個 addEventListener 我們就可以知道每一張圖片是否已經載入。

那麼這邊只需要搭配一些判斷就可以達到當圖片全部載入後才才顯示,例如這樣調整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const imgID = document.querySelectorAll('.img');

let i = 0;
imgID.forEach((item, index) => {
const img = new Image();
img.src = item.src;

img.addEventListener("load", () => {
i += 1; // 讀取完畢就 + 1
if(imgID.length === i) { // 當圖片全部讀取完畢就顯示載入完成,這邊可以搭配 Loading 狀態切換畫面
console.log('圖片全部載入完成');
}
});
})

除此之外 new Image 還有另一個功能,可以用來判斷圖片是否成功載入,假使圖片載入失敗,那麼我們也可以先用「預設圖片」來頂替,舉例來講以下就有一張圖片是死掉的

1
2
3
4
5
6
7
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://firebasestorage.googleapis.com/v0/b/welcomewebworld-4097b.appspot.com/o/blogImg%2FJavaScript%2F%E6%88%AA%E5%9C%96%202020-11-20%2022.18.14.png?alt=media&token=2d9c3a62-eef4-4fa0-b97a-259cc303f570" class="img"> // 死圖
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
<img src="https://firebasestorage.googleapis.com/v0/b/welcomewebworld-4097b.appspot.com/o/blogImg%2FJavaScript%2F%E6%88%AA%E5%9C%96%202020-11-20%2022.18.14.png?alt=media&token=2d9c3a62-eef4-4fa0-b97a-259cc303f570" class="img"> // 死圖
<img src="https://images.unsplash.com/photo-1608607261815-84bcae9cad8f?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" class="img">
1
2
3
4
img {
width: 100px;
height: 100px;
}

在此我們就會使用到 new Image 中的一個屬性也就是 complete,假使圖片已經有開始成功被下載,那麼就會是 true,通常來講如果圖片是 404 就會無法下載,那麼 complete 就會是 false,因此我們就可以調整成以下

1
2
3
4
5
6
7
8
9
10
11
const imgID = document.querySelectorAll('.img');

const defaultImg = 'https://i.imgur.com/cFOdLy8.png'; // 預設圖片

imgID.forEach((item, index) => {
const img = new Image();
img.src = item.src;
if (!img.complete) {
item.src = defaultImg;
}
})

See the Pen img onload(1) by Ray (@hsiangfeng) on CodePen.

當然這個技巧也可以活用在背景圖上,只是因為背景圖的作法通常會因為多了雙引號以及括號而出現問題,所以通常就會使用 .replace(/\"/g, ""); 處理,完整範例如下

1
2
<div class="img bg-cover" style="background-image: url('https://i.imgur.com/xAVBpW0.png'); width: 100px; height: 100px;"></div>
<div class="img bg-cover" style="background-image: url('https://firebasestorage.googleapis.com/v0/b/welcomewebworld-4097b.appspot.com/o/blogImg%2FJavaScript%2F%E6%88%AA%E5%9C%96%202020-11-20%2022.18.14.png?alt=media&token=2d9c3a62-eef4-4fa0-b97a-259cc303f570'); width: 100px; height: 100px;"></div> // 死圖
1
2
3
4
.bg-cover {
background-size: cover;
background-position: center center;
}

最主要在取得背景圖時要需注意,要先使用 split 切割出前後花括號取得裡面的 url

1
2
3
4
5
6
7
8
9
10
11
12
13
const imgID = document.querySelectorAll('.img');

const defaultImg = 'https://i.imgur.com/cFOdLy8.png';

imgID.forEach((item) => {
const imgUrl = item.style.backgroundImage.split("(")[1].split(")")[0]; // 取的 url
const image = new Image();
image.src = imgUrl.replace(/\"/g, ""); // 背景圖取得出來之後會有雙引號,因此要去除這個雙引號
if (!image.complete) {
item.style.backgroundImage = `url(${defaultImg})`
}
image.onload = () => item.style.backgroundImage = `url(${imgUrl})`;
})

透過以上方式就可以解決如果當使用者上傳的是一張 404 的圖片,那麼就可以載入預設圖片囉。

See the Pen img onload(2) by Ray (@hsiangfeng) on CodePen.

參考文獻