JavaScript實(shí)現(xiàn)圖片懶加載的三種常用方法總結(jié)
1.前言
1.1 什么是使用圖片懶加載
懶加載是一種對(duì)網(wǎng)頁(yè)性能優(yōu)化的方式,比如當(dāng)訪問一個(gè)網(wǎng)頁(yè)的時(shí)候,優(yōu)先顯示可視區(qū)域的圖片而不是一次加載全部的圖片,當(dāng)需要顯示時(shí),再發(fā)送請(qǐng)求加載圖片。
1.2 為什么使用圖片懶加載
- 避免首次加載時(shí)消耗大量時(shí)間,降低頁(yè)面渲染速度,造成卡頓現(xiàn)象。
- 按需加載,避免無效圖片的加載,減輕服務(wù)器壓力,節(jié)約網(wǎng)絡(luò)資源。
若不使用圖片懶加載,頁(yè)面啟動(dòng)時(shí),會(huì)加載全部的圖片資源:

1.3 圖片懶加載的實(shí)現(xiàn)原理
1 基本原理: 監(jiān)聽圖片是否位于頁(yè)面的可視區(qū)域內(nèi),若在則加載圖片,不在則不加載圖片
2 實(shí)現(xiàn)方案: 自定義屬性-將圖片真實(shí)地址 url 存儲(chǔ)在自定義屬性中,當(dāng)監(jiān)聽到圖片進(jìn)入可視區(qū)域時(shí),將自定義屬性值賦值給 img 的 src 屬性
2.實(shí)現(xiàn)方法
2.1 利用元素的 getBoundingClientRect 方法實(shí)現(xiàn)
(1)屬性介紹:
利用.getBoundingClientRect實(shí)時(shí)獲取物體的動(dòng)態(tài)位置

(2)實(shí)現(xiàn)方法:
步驟 1:監(jiān)聽頁(yè)面滾動(dòng)事件,lazyLoad為頁(yè)面滾動(dòng)時(shí)的處理函數(shù),在本節(jié)為處理圖片懶加載。
window.addEventListener('scroll', lazyLoad)步驟 2:判斷圖片是否處于可視區(qū)域內(nèi)
(1)若距離頂部top小于頁(yè)面的整體高度window.innerHeight
(2)若距離左側(cè)left小于頁(yè)面的整體寬度window.innerWidth
(3)同時(shí)圖片的底部bottom與圖片的右側(cè)right 距頁(yè)面頂部、左側(cè)的距離均大于0
則說明該圖在屏幕的可視區(qū)域內(nèi)。

為了提高復(fù)用性,我們可以將它封裝成一個(gè)自定義函數(shù)isVisible,將每張圖片作為參數(shù)傳入該函數(shù),并返回true或false
// 可視區(qū)域判斷函數(shù)
function isVisible(img) {
// 判斷是否在可視區(qū)域,并返回true或false
const imgRect = img.getBoundingClientRect() // getBoundingClientRect 獲取圖片的動(dòng)態(tài)信息
return imgRect.bottom > 0 && imgRect.top < window.innerHeight && imgRect.right > 0 && imgRect.left < window.innerWidth
}步驟 3:定義圖片懶加載時(shí)的處理事件,監(jiān)聽所有的img,判斷該img是否處于可視范圍內(nèi)
querySelectorAll 獲取的元素為偽數(shù)組 需要轉(zhuǎn)為真數(shù)組,否則無法使用數(shù)組的某些方法
// 獲取所有的img元素,并利用擴(kuò)展運(yùn)算符轉(zhuǎn)為真數(shù)組
const images = [...document.querySelectorAll('img')] 
步驟 4:對(duì)每張圖片進(jìn)行監(jiān)聽,利用自定義函數(shù)isVisible判斷是否在可視區(qū)域內(nèi)
(1)若處于可視區(qū)域:將自定義的data-src值,賦值給真正的src屬性值,其中 data-src存儲(chǔ)圖片的URL地址,并刪除該元素防止重復(fù)加載
(2)若不處于可視區(qū)域:return 不做處理
// 利用循環(huán)判斷每張圖片是否屬于可視區(qū)域
function lazyLoad(){
for (let i = 0; i < images.length; i++) {
// isVisible是否該圖片位于可視區(qū)域 返回true 或false
if (isVisible(images[i])) {
// 將元素的自定義屬性 data-src 賦值給元素的 src 屬性
// 等價(jià)于:img.setAttribute('src', img.getAttribute('data-src'))
images[i].src = images[i].dataset.src
// 防止重復(fù)被遍歷 加載完之后 刪除元素不再加載
images.splice(i, 1)
i--
}
}
}
lazyLoad()(3)整體代碼:
// html 標(biāo)簽結(jié)構(gòu) <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默認(rèn).jpg" alt="">
// 1 獲取全部圖片的DOM節(jié)點(diǎn)
// 注意:querySelectorAll 值為偽數(shù)組利用擴(kuò)展運(yùn)算符轉(zhuǎn)為真數(shù)組
const images = [...document.querySelectorAll('img')]
// 2 監(jiān)聽頁(yè)面滾動(dòng)事件
window.addEventListener('scroll', lazyLoad)
// 3 定義頁(yè)面滾動(dòng)的處理函數(shù)
function lazyLoad(){
for (let i = 0; i < images.length; i++) {
// isVisible是否該圖片位于可視區(qū)域 返回true 或false
if (isVisible(images[i])) {
// 將元素的自定義屬性 data-src 賦值給元素的 src 屬性
// dataset.src 此為元素的自定義屬性 data-src
images[i].src = images[i].dataset.src // 等價(jià)于:img.setAttribute('src', img.getAttribute('data-src'))
// 防止重復(fù)被遍歷 加載完之后 刪除元素不再加載
images.splice(i, 1)
i--
}
}
}
lazyLoad()
// 4 可視區(qū)域判斷函數(shù)
function isVisible(img) {
// 判斷是否在可視區(qū)域
const imgRect = img.getBoundingClientRect() // getBoundingClientRect 獲取圖片的動(dòng)態(tài)信息
return imgRect.bottom > 0 && imgRect.top < window.innerHeight && imgRect.right > 0 && imgRect.left < window.innerWidth
}2.2 利用整體距離實(shí)現(xiàn)
(1)屬性介紹:
clientHeight : 網(wǎng)頁(yè)可見區(qū)域高
- A 表示可見區(qū)域的高度,包含
padding不包含border和margin - B 語(yǔ)法:
element.clientHeight - C 備注:
body.clientHeight=window.innerHeight
innertHeight : window 整體高度
- A 表示
window的內(nèi)部高度,包括縱向滾動(dòng)條 - B 語(yǔ)法:
window.innertHeight
offsetTop : 距離父級(jí)元素頂部的高度
- A 表示當(dāng)前元素相對(duì)于其
offsetParent元素的頂部?jī)?nèi)邊距的距離 - B 語(yǔ)法:
element.offsetTop
scrollTop : 網(wǎng)頁(yè)被卷去的距離
- A 表示在有滾動(dòng)條時(shí),滾動(dòng)條向下滾動(dòng)的距離也就是元素頂部被遮住部分的高度
- B 語(yǔ)法:
element.scrollTop
(2)實(shí)現(xiàn)方法:
可以用image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop 判斷圖片是否可以在可視區(qū)域內(nèi)。
- 圖片元素位置的頂部距離:
offsetTop - 滾動(dòng)距離的最下端:
scrollTop+clientHeight
// html 標(biāo)簽結(jié)構(gòu) <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默認(rèn).jpg" alt="">
// 1 獲取全部圖片的DOM節(jié)點(diǎn)
// 注意:querySelectorAll 值為偽數(shù)組利用擴(kuò)展運(yùn)算符轉(zhuǎn)為真數(shù)組
const images = [...document.querySelectorAll('img')]
// 2 監(jiān)聽頁(yè)面滾動(dòng)事件
window.addEventListener('scroll', lazyLoad)
// 3 定義頁(yè)面滾動(dòng)的處理函數(shù)
function lazyload(e){
// 3.1 獲取屏幕的可視高度
const clientHeight = document.documentElement.clientHeight
// 3.2 獲取屏幕的滾動(dòng)距離
const scrollTop = document.documentElement.scrollTop
for (let i = 0; i < images.length; i++) {
if (images[i].offsetTop < clientHeight + scrollTop) {
images[i].setAttribute('src', images[i].getAttribute('data-src'))
}
}
}2.3 利用Intersection Observer實(shí)現(xiàn)
Intersection Observer是一個(gè)比較新的api,他允許你追蹤目標(biāo)元素與其祖先元素或視窗的交叉狀態(tài),用他來檢測(cè)圖片是否進(jìn)入視口非常方便,不用再像之前綁定事件、計(jì)算距離等。
(1)屬性介紹:
- 利用
Intersection Observer實(shí)例上的observe和unobserve方法,注冊(cè)或取消監(jiān)聽事件。 - 利用
isIntersecting方法,判斷該圖片是否處于圖片與屏幕可視區(qū)域的交叉范圍內(nèi)。 - 注意:
Intersection Observer實(shí)例會(huì)監(jiān)聽交叉狀態(tài),即出現(xiàn)和消失(觸發(fā)兩次),出現(xiàn)交叉狀態(tài)后會(huì)去調(diào)用new的時(shí)候傳入的callback回調(diào)函數(shù)
(2)實(shí)現(xiàn)方法:
步驟 1: 監(jiān)聽頁(yè)面滾動(dòng)事件,lazyLoad為頁(yè)面滾動(dòng)時(shí)的處理函數(shù),在本節(jié)為處理圖片懶加載。
window.addEventListener('scroll', lazyLoad)步驟 2: 創(chuàng)建圖片與可視區(qū)域交叉實(shí)例
callback:
- 此為傳入的回調(diào)函數(shù),用于當(dāng)處于交叉狀態(tài)改變時(shí)進(jìn)行的處理函數(shù)
- 該函數(shù)會(huì)被觸發(fā)2次:圖片進(jìn)入視野時(shí)+圖片離開視野時(shí)
const observer = new IntersectionObserver(callback)
步驟 3: 利用observer實(shí)例上的.observe(img)方法,給每張圖片綁定觀察事件
// 給每一個(gè)圖片綁定觀察方法
imagess.forEach(img => {
// 圖片進(jìn)入視野+離開視野時(shí)會(huì)觸發(fā)callback回調(diào)函數(shù)
observer.observe(img)
})步驟 4: 定義圖片的懶加載事件
imgArr:
- 可以獲得包含所有圖片的
isIntersecting屬性的集合,該屬性可判斷是否在交叉區(qū)域內(nèi) target為該圖片的標(biāo)簽元素

// callback 接收的參數(shù)為帶有監(jiān)聽所有圖片交叉屬性的集合
const callback = (imgArr) => {
console.log('視圖交叉時(shí)觸發(fā),離開交叉時(shí)也觸發(fā)', imgArr)
imgArr.forEach(e => {
// 判斷是否在視野區(qū)域
if (e.isIntersecting) {
e.target.src = e.target.dataset.src
// 取消監(jiān)聽,避免重復(fù)加載同一張圖片
observer.unobserve(e.target)
}
})
}(3)整體代碼:
// html 標(biāo)簽結(jié)構(gòu) <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默認(rèn).jpg" alt=""> <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默認(rèn).jpg" alt="">
// intersectionObserver 交叉觀察 : 目標(biāo)元素和可視窗口會(huì)產(chǎn)生交叉區(qū)域
const imagess = [...document.querySelectorAll('img')]
// 2.1 創(chuàng)建視覺交叉的觀察實(shí)例
const observer = new IntersectionObserver(callback)
// 2.2 給每一個(gè)圖片綁定觀察方法
imagess.forEach(img => {
// 2.3 圖片進(jìn)入視野+離開視野時(shí)觸發(fā) - 回調(diào)
observer.observe(img)
})
// callback 接收的參數(shù)為帶有監(jiān)聽所有圖片交叉屬性的集合
const callback = (imgArr) => {
console.log('視圖交叉時(shí)觸發(fā),離開交叉時(shí)也觸發(fā)', imgArr) // imgArr為
imgArr.forEach(e => {
// 判斷是否在視野區(qū)域
if (e.isIntersecting) {
e.target.src = e.target.dataset.src
// 取消觀察追蹤,避免重復(fù)加載同一張圖片
observer.unobserve(e.target)
}
})
}以上就是JavaScript實(shí)現(xiàn)圖片懶加載的三種常用方法總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript圖片懶加載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
十個(gè)免費(fèi)的web前端開發(fā)工具詳細(xì)整理
這篇文章主要介紹了十個(gè)免費(fèi)的web前端開發(fā)工具詳細(xì)整理的相關(guān)資料,希望通過本文大家能夠理解使用免費(fèi)web開發(fā)工具,需要的朋友可以參考下2017-09-09
JavaScript中將字符串轉(zhuǎn)換為浮點(diǎn)數(shù)的技巧
在JavaScript中,parseFloat是一個(gè)內(nèi)置函數(shù),用于將一個(gè)字符串解析成浮點(diǎn)數(shù),這個(gè)方法對(duì)于處理用戶輸入、從文本文件讀取數(shù)據(jù)或者在Web API中獲取數(shù)值尤其重要,本文將詳細(xì)介紹parseFloat的工作原理、使用方法以及通過代碼案例展示其在實(shí)際開發(fā)中的應(yīng)用2025-02-02
jsp網(wǎng)頁(yè)搜索結(jié)果中實(shí)現(xiàn)選中一行使其高亮
在做搜索結(jié)果時(shí)為了好看需要將選中的行變的高亮些,下面有個(gè)不錯(cuò)的示例,需要的朋友可以參考下2014-02-02
通過Jscript中@cc_on 語(yǔ)句識(shí)別IE瀏覽器及版本的代碼
通過Jscript中@cc_on 語(yǔ)句識(shí)別IE瀏覽器及版本的代碼,需要的朋友可以參考下。2011-05-05
Sortable.js功能強(qiáng)大的JavaScript 拖拽庫(kù)示例詳解
SortableJS 是一個(gè)強(qiáng)大、靈活且易于使用的 JavaScript 庫(kù),適用于各種類型的拖放排序需求,這篇文章主要介紹了Sortable.js功能強(qiáng)大的JavaScript 拖拽庫(kù)示例詳解,需要的朋友可以參考下2025-01-01

