原生JS實(shí)現(xiàn)最簡(jiǎn)單的圖片懶加載

轉(zhuǎn)載地址:https://segmentfault.com/a/1190000010744417
懶加載
什么是懶加載
懶加載其實(shí)就是延遲加載,是一種對(duì)網(wǎng)頁(yè)性能優(yōu)化的方式,比如當(dāng)訪問(wèn)一個(gè)頁(yè)面的時(shí)候翘县,優(yōu)先顯示可視區(qū)域的圖片而不一次性加載所有圖片杀饵,當(dāng)需要顯示的時(shí)候再發(fā)送圖片請(qǐng)求系吭,避免打開(kāi)網(wǎng)頁(yè)時(shí)加載過(guò)多資源脉漏。

什么時(shí)候用懶加載
當(dāng)頁(yè)面中需要一次性載入很多圖片的時(shí)候苞冯,往往都是需要用懶加載的。

懶加載原理
我們都知道HTML中的<img>標(biāo)簽是代表文檔中的一個(gè)圖像鸠删。抱完。說(shuō)了個(gè)廢話。刃泡。

<img>標(biāo)簽有一個(gè)屬性是src巧娱,用來(lái)表示圖像的URL,當(dāng)這個(gè)屬性的值不為空時(shí)烘贴,瀏覽器就會(huì)根據(jù)這個(gè)值發(fā)送請(qǐng)求禁添。如果沒(méi)有src屬性,就不會(huì)發(fā)送請(qǐng)求桨踪。

嗯老翘?貌似這點(diǎn)可以利用一下?

我先不設(shè)置src锻离,需要的時(shí)候再設(shè)置铺峭?

nice,就是這樣汽纠。

我們先不給<img>設(shè)置src卫键,把圖片真正的URL放在另一個(gè)屬性data-src中,在需要的時(shí)候也就是圖片進(jìn)入可視區(qū)域的之前虱朵,將URL取出放到src中莉炉。

實(shí)現(xiàn)
HTML結(jié)構(gòu)

<div class="container">
  <div class="img-area">
    ![](./img/img1.png)
  </div>
  <div class="img-area">
    ![](./img/img2.png)
  </div>
  <div class="img-area">
    ![](./img/img3.png)
  </div>
  <div class="img-area">
    ![](./img/img4.png)
  </div>
  <div class="img-area">
    ![](./img/img5.png)
  </div>
</div>

仔細(xì)觀察一下,<img>標(biāo)簽此時(shí)是沒(méi)有src屬性的碴犬,只有alt和data-src屬性絮宁。

alt 屬性是一個(gè)必需的屬性,它規(guī)定在圖像無(wú)法顯示時(shí)的替代文本服协。
data-* 全局屬性:構(gòu)成一類名稱為自定義數(shù)據(jù)屬性的屬性绍昂,可以通過(guò)HTMLElement.dataset來(lái)訪問(wèn)。

如何判斷元素是否在可視區(qū)域
方法一
網(wǎng)上看到好多這種方法偿荷,稍微記錄一下治专。
通過(guò)document.documentElement.clientHeight
獲取屏幕可視窗口高度
通過(guò)element.offsetTop
獲取元素相對(duì)于文檔頂部的距離
通過(guò)document.documentElement.scrollTop
獲取瀏覽器窗口頂部與文檔頂部之間的距離,也就是滾動(dòng)條滾動(dòng)的距離

然后判斷②-③<①是否成立遭顶,如果成立张峰,元素就在可視區(qū)域內(nèi)。
方法二(推薦)
通過(guò)getBoundingClientRect()
方法來(lái)獲取元素的大小以及位置棒旗,MDN上是這樣描述的:
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.

這個(gè)方法返回一個(gè)名為ClientRect
的DOMRect
對(duì)象喘批,包含了top
撩荣、right
、botton
饶深、left
餐曹、width
、height
這些值敌厘。
MDN上有這樣一張圖:

image.png

可以看出返回的元素位置是相對(duì)于左上角而言的台猴,而不是邊距。
我們思考一下俱两,什么情況下圖片進(jìn)入可視區(qū)域饱狂。
假設(shè)const bound = el.getBoundingClientRect();
來(lái)表示圖片到可視區(qū)域頂部距離;并設(shè) const clientHeight = window.innerHeight;
來(lái)表示可視區(qū)域的高度宪彩。
隨著滾動(dòng)條的向下滾動(dòng)休讳,bound.top
會(huì)越來(lái)越小,也就是圖片到可視區(qū)域頂部的距離越來(lái)越小尿孔,當(dāng)bound.top===clientHeight
時(shí)俊柔,圖片的上沿應(yīng)該是位于可視區(qū)域下沿的位置的臨界點(diǎn),再滾動(dòng)一點(diǎn)點(diǎn)活合,圖片就會(huì)進(jìn)入可視區(qū)域雏婶。
也就是說(shuō),在bound.top<=clientHeight
時(shí)白指,圖片是在可視區(qū)域內(nèi)的尚骄。
我們這樣判斷:

function isInSight(el) {
  const bound = el.getBoundingClientRect();
  const clientHeight = window.innerHeight;
  //如果只考慮向下滾動(dòng)加載
  //const clientWidth = window.innerWeight;
  return bound.top <= clientHeight + 100;
}

這里有個(gè)+100是為了提前加載。

經(jīng)提醒侵续。。這個(gè)方法性能

加載圖片
頁(yè)面打開(kāi)時(shí)需要對(duì)所有圖片進(jìn)行檢查憨闰,是否在可視區(qū)域內(nèi)状蜗,如果是就加載。

function checkImgs() {
  const imgs = document.querySelectorAll('.my-photo');
  Array.from(imgs).forEach(el => {
    if (isInSight(el)) {
      loadImg(el);
    }
  })
}

function loadImg(el) {
  if (!el.src) {
    const source = el.dataset.src;
    el.src = source;
  }
}

這里應(yīng)該是有一個(gè)優(yōu)化的地方鹉动,設(shè)一個(gè)標(biāo)識(shí)符標(biāo)識(shí)已經(jīng)加載圖片的index轧坎,當(dāng)滾動(dòng)條滾動(dòng)時(shí)就不需要遍歷所有的圖片,只需要遍歷未加載的圖片即可泽示。

函數(shù)節(jié)流
在類似于滾動(dòng)條滾動(dòng)等頻繁的DOM操作時(shí)缸血,總會(huì)提到“函數(shù)節(jié)流、函數(shù)去抖”械筛。

所謂的函數(shù)節(jié)流捎泻,也就是讓一個(gè)函數(shù)不要執(zhí)行的太頻繁,減少一些過(guò)快的調(diào)用來(lái)節(jié)流埋哟。

基本步驟:

獲取第一次觸發(fā)事件的時(shí)間戳
獲取第二次觸發(fā)事件的時(shí)間戳
時(shí)間差如果大于某個(gè)閾值就執(zhí)行事件笆豁,然后重置第一個(gè)時(shí)間

function throttle(fn, mustRun = 500) {
  const timer = null;
  let previous = null;
  return function() {
    const now = new Date();
    const context = this;
    const args = arguments;
    if (!previous){
      previous = now;
    }
    const remaining = now - previous;
    if (mustRun && remaining >= mustRun) {
      fn.apply(context, args);
      previous = now;
    }
  }
}

這里的mustRun就是調(diào)用函數(shù)的時(shí)間間隔,無(wú)論多么頻繁的調(diào)用fn,只有remaining>=mustRun時(shí)fn才能被執(zhí)行闯狱。

方法三 IntersectionObserver
經(jīng)大佬提醒煞赢,發(fā)現(xiàn)了這個(gè)方法
先附上鏈接:
jjc大大:https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10
阮一峰大大:http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
API Sketch for Intersection Observers:https://github.com/WICG/IntersectionObserver
IntersectionObserver
可以自動(dòng)觀察元素是否在視口內(nèi)。

var io = new IntersectionObserver(callback, option);
// 開(kāi)始觀察
io.observe(document.getElementById('example'));
// 停止觀察
io.unobserve(element);
// 關(guān)閉觀察器
io.disconnect();

callback的參數(shù)是一個(gè)數(shù)組哄孤,每個(gè)數(shù)組都是一個(gè)IntersectionObserverEntry對(duì)象照筑,包括以下屬性:

屬性 描述
time 可見(jiàn)性發(fā)生變化的時(shí)間,單位為毫秒
rootBounds 與getBoundingClientRect()方法的返回值一樣
boundingClientRect 目標(biāo)元素的矩形區(qū)域的信息
intersectionRect 目標(biāo)元素與視口(或根元素)的交叉區(qū)域的信息
intersectionRatio 目標(biāo)元素的可見(jiàn)比例瘦陈,即intersectionRect占boundingClientRect的比例凝危,完全可見(jiàn)時(shí)為1,完全不可見(jiàn)時(shí)小于等于0
target 被觀察的目標(biāo)元素双饥,是一個(gè) DOM 節(jié)點(diǎn)對(duì)象
我們需要用到intersectionRatio來(lái)判斷是否在可視區(qū)域內(nèi)媒抠,當(dāng)intersectionRatio > 0 && intersectionRatio <= 1即在可視區(qū)域內(nèi)。

代碼

function checkImgs() {
  const imgs = Array.from(document.querySelectorAll(".my-photo"));
  imgs.forEach(item => io.observe(item));
}

function loadImg(el) {
  if (!el.src) {
    const source = el.dataset.src;
    el.src = source;
  }
}

const io = new IntersectionObserver(ioes => {
  ioes.forEach(ioe => {
    const el = ioe.target;
    const intersectionRatio = ioe.intersectionRatio;
    if (intersectionRatio > 0 && intersectionRatio <= 1) {
      loadImg(el);
    }
    el.onload = el.onerror = () => io.unobserve(el);
  });
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咏花,一起剝皮案震驚了整個(gè)濱河市趴生,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昏翰,老刑警劉巖苍匆,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異棚菊,居然都是意外死亡浸踩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)统求,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)检碗,“玉大人,你說(shuō)我怎么就攤上這事码邻≌厶辏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵像屋,是天一觀的道長(zhǎng)怕犁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)己莺,這世上最難降的妖魔是什么奏甫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮凌受,結(jié)果婚禮上阵子,老公的妹妹穿的比我還像新娘。我一直安慰自己胜蛉,他們只是感情好款筑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布智蝠。 她就那樣靜靜地躺著,像睡著了一般奈梳。 火紅的嫁衣襯著肌膚如雪杈湾。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天攘须,我揣著相機(jī)與錄音漆撞,去河邊找鬼。 笑死于宙,一個(gè)胖子當(dāng)著我的面吹牛浮驳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捞魁,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼至会,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了谱俭?” 一聲冷哼從身側(cè)響起奉件,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昆著,沒(méi)想到半個(gè)月后县貌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凑懂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年煤痕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片接谨。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摆碉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脓豪,到底是詐尸還是另有隱情巷帝,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布跑揉,位于F島的核電站,受9級(jí)特大地震影響埠巨,放射性物質(zhì)發(fā)生泄漏历谍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一辣垒、第九天 我趴在偏房一處隱蔽的房頂上張望望侈。 院中可真熱鬧,春花似錦勋桶、人聲如沸脱衙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捐韩。三九已至退唠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荤胁,已是汗流浹背瞧预。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仅政,地道東北人垢油。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像圆丹,于是被迫代替她去往敵國(guó)和親滩愁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 問(wèn)答題47 /72 常見(jiàn)瀏覽器兼容性問(wèn)題與解決方案辫封? 參考答案 (1)瀏覽器兼容問(wèn)題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,754評(píng)論 1 92
  • 1硝枉、懶加載 1.什么是懶加載? 懶加載也就是延遲加載秸讹。當(dāng)訪問(wèn)一個(gè)頁(yè)面的時(shí)候檀咙,先把img元素或是其他元素的背景圖片路...
    xiaolizhenzhen閱讀 70,474評(píng)論 18 160
  • 1、懶加載1.什么是懶加載璃诀?懶加載也就是延遲加載弧可。當(dāng)訪問(wèn)一個(gè)頁(yè)面的時(shí)候,先把img元素或是其他元素的背景圖片路徑替...
    Gaochengxin閱讀 375評(píng)論 1 2
  • Window和document對(duì)象的區(qū)別 window對(duì)象window對(duì)象表示瀏覽器中打開(kāi)的窗口window對(duì)象是...
    FConfidence閱讀 2,209評(píng)論 0 5
  • 在今年9月13日劣欢,iPhone發(fā)布十周年之際棕诵,蘋(píng)果隆重推出了十周年紀(jì)念版,iPhone X凿将。 蘋(píng)果稱這是一款面向未...
    kyree閱讀 371評(píng)論 0 1