使用IntersectionObserver來(lái)在提升一下性能

一直以來(lái)我們要監(jiān)控2個(gè)元素的相對(duì)位置,總是比較麻煩的,而且之前也只能通過(guò)js以及每個(gè)元素的top值來(lái)控制,這也極易拖慢整個(gè)網(wǎng)站的性能。然而,隨著網(wǎng)頁(yè)的發(fā)展,對(duì)上述檢測(cè)的需求也隨之增加,多種情況下都需要用到元素交集變化的信息。如:

  • 當(dāng)頁(yè)面滾動(dòng)時(shí),懶加載圖片或其他內(nèi)容。
  • 實(shí)現(xiàn)”無(wú)限滾動(dòng)“功能頁(yè)面
  • 可以統(tǒng)計(jì)一些廣告元素的曝光情況
  • 根據(jù)用戶滾動(dòng)位置來(lái)控制執(zhí)行任務(wù)或者動(dòng)畫(huà)

相對(duì)于過(guò)去,我們?cè)跈z測(cè)交集時(shí),需要涉及到事件監(jiān)聽(tīng),以及對(duì)每個(gè)目標(biāo)元素執(zhí)行Element.getBoundingClientRect()方法來(lái)獲取所需信息。然后通過(guò)每個(gè)元素的信息來(lái)檢測(cè)元素的交叉。

而且在類似無(wú)限滾動(dòng)的場(chǎng)景下,我們不僅僅需要獲取一些元素的位置信息,更需要監(jiān)控例如scroll等事件,并且很多情況下,也需要依賴一些第三方庫(kù)來(lái)進(jìn)行監(jiān)控,并且在第三方庫(kù)中具體執(zhí)行了什么,我們并不知曉,這樣很是影響性能,并且得到的體驗(yàn)也不是特別友好。

那在這種背景下,我們有更好的方法嗎?

IntersectionObserver概念

Intersection Observer的出現(xiàn),解決了這個(gè)問(wèn)題,Intersection Observer API 會(huì)在瀏覽器注冊(cè)一個(gè)觀察者,并且可以設(shè)定據(jù)地要觀察的目標(biāo)(target),當(dāng)目標(biāo)元素(target)以及根元素或者指定的外層元素(root元素)相互交叉的時(shí)候觸發(fā)事件。

用法介紹

//首先我們要先創(chuàng)建觀察者
var observer = new IntersectionObserver(callback, options);
//接下來(lái)我們要設(shè)置具體觀察哪個(gè)目標(biāo)
let target = document.querySelector('#id');
observer.observe(target);

我們?cè)趤?lái)看看創(chuàng)建監(jiān)控函數(shù)時(shí)要傳遞的options中的參數(shù):

參數(shù)名 描述 類型 默認(rèn)值
root 指定根目錄,也就是當(dāng)目標(biāo)元素顯示在這個(gè)元素中時(shí)會(huì)觸發(fā)監(jiān)控回調(diào) Dom元素 null,即瀏覽器窗口
rootMargin 類似于css的margin,設(shè)定root元素的邊框區(qū)域。值與css的margin一樣“10px 10px 10px 10px” 對(duì)應(yīng)“top right bottom left" String 0
threshold 可以是單一的number也可以是一個(gè)number數(shù)組,這個(gè)值可以控制target元素進(jìn)入root元素中可見(jiàn)性超過(guò)的闕值,當(dāng)達(dá)到這個(gè)值則會(huì)出發(fā)函數(shù),并且我們也可以使用數(shù)據(jù)來(lái)讓元素在進(jìn)入時(shí)在不同的可見(jiàn)度返回多次值 number或array 0

實(shí)戰(zhàn)

我們先來(lái)看一個(gè)最簡(jiǎn)單的操作:
這是我們的html:

//我們創(chuàng)建三個(gè)容器,并且把第三個(gè)容器中的p定位目標(biāo)
 <div class="con">
     <div class="page red">
         <p>我是第一頁(yè)</p>
     </div>
     <div id="scrollArea" class="page green">
         <p>我是第二頁(yè)</p>
     </div>
     <div class="page blue">
         <p class="listItem">我是第三頁(yè)</p>
     </div>
</div>

接下來(lái)我們創(chuàng)建觀察者,以及設(shè)定要觀察的目標(biāo):

var observer = new IntersectionObserver((entries, observer) => {
    alert("進(jìn)入");
}, {});
let target = document.querySelector('.listItem');
observer.observe(target);
全部使用默認(rèn)參數(shù)的簡(jiǎn)單效果

我們來(lái)設(shè)定一下對(duì)應(yīng)的root,讓target的目標(biāo)換成一個(gè)指定的元素。

//我們?cè)O(shè)定一個(gè)外層容器,并且把這個(gè)容器先搞小一點(diǎn),方便查看:
var options = {
    root: document.querySelector('.con')
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("進(jìn)入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

我們可以指定對(duì)應(yīng)的被觀察者要進(jìn)入哪個(gè)root根容器中才會(huì)觸發(fā)回調(diào)。

我們?cè)O(shè)定了一個(gè)root值之后的效果

接下來(lái)我們?cè)趤?lái)嘗試更復(fù)雜的去控制:
需要注意的是,當(dāng)我們這樣來(lái)設(shè)置rootMargin時(shí),會(huì)出現(xiàn)錯(cuò)誤:

var options = {
    rootMargin: "100px 0 0 0"
}
rootMargin設(shè)置錯(cuò)誤

雖然其規(guī)范與css得我margin一樣,但是當(dāng)值是0的時(shí)候,我們是無(wú)法直接使用0的,要添加上單位,如px、rem、em等,正確寫(xiě)法如下:

var options = {
    rootMargin: "1
00px 0px 0px 0px"
}

我們來(lái)看一下這個(gè)圖,當(dāng)設(shè)置rootMargin時(shí),就相當(dāng)于把對(duì)應(yīng)的元素放大,如下圖,root為黃色區(qū)域,但是我們?cè)O(shè)定了rootMargin,實(shí)際上對(duì)于target來(lái)說(shuō),整個(gè)藍(lán)色區(qū)域都會(huì)認(rèn)定為root的監(jiān)控區(qū)域,當(dāng)target進(jìn)入藍(lán)色區(qū)域機(jī)會(huì)觸發(fā)回調(diào)。

rootmargin中root與target關(guān)系

讓我們來(lái)看看如何設(shè)定吧:

var options = {
    root: document.querySelector('.con'),
    rootMargin: "0px 0px 100px 0px"
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("進(jìn)入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

注意:我們?cè)O(shè)置的margin是root的而不是target的,所以在一下的這個(gè)demo中,當(dāng)target往上滑動(dòng)時(shí),target具體root還差100px時(shí)就觸發(fā)了回調(diào),這個(gè)要設(shè)定為bottom而非top。

設(shè)置rootMargin之后的效果

我們來(lái)看看threshold屬性:

threshold 此參數(shù)的范圍為0.0-1.0,并且我們可以設(shè)置一個(gè)number值,也可以設(shè)置一個(gè)number的Array數(shù)組,來(lái)觸發(fā)多次回調(diào)。

首先我們?cè)O(shè)定一個(gè)觸發(fā)值:

var options = {
    root: document.querySelector('.con'),
    threshold: 1.0
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("進(jìn)入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

我們?cè)O(shè)定,當(dāng)target全部顯示到root中時(shí)才會(huì)觸發(fā)回調(diào):

設(shè)定了一個(gè)值

這次我們來(lái)嘗試設(shè)置多次觸發(fā)點(diǎn):

var options = {
    root: document.querySelector('.con'),
    threshold: [0, 0.5, 1.0]
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("進(jìn)入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

我們?cè)O(shè)定了在target開(kāi)始出現(xiàn),出現(xiàn)一半,以及全部漏出的時(shí)候都會(huì)觸發(fā),所以應(yīng)該會(huì)觸發(fā)三次回調(diào),看看效果呢。

多個(gè)觸發(fā)點(diǎn)控制

截止到此,我們應(yīng)該已經(jīng)知道IntersectionObserver的使用方法。

IntersectionObserver回調(diào)中的回參

既然我們知道此API的使用方式,那么我們也一定很好奇此函數(shù)具體返回了什么。
函數(shù)的callback會(huì)返回2個(gè)值: [IntersectionObserverEntry] 和 IntersectionObserver。

  1. IntersectionObserverEntry: 提供了target和root交叉之后的一些信息,此值無(wú)法主動(dòng)創(chuàng)建,但是可以通過(guò)IntersectionObserver.takeRecords()來(lái)獲取。
    參數(shù)內(nèi)容如下:

    IntersectionObserverEntry

  2. IntersectionObserver: 提供了當(dāng)前創(chuàng)建的觀察者IntersectionObserver的所有信息。

    IntersectionObserver

尾聲

這個(gè)API,可能大家遇到或者聽(tīng)說(shuō)過(guò)的都不多,不過(guò)如果有幸你們看到了它,那它可能會(huì)給你的代碼帶來(lái)一些新的優(yōu)化靈感,如果能達(dá)到這樣的效果,那么這篇文章就有了其存在的價(jià)值。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,658評(píng)論 1 32
  • ??JavaScript 與 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的。 ??事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,695評(píng)論 1 11
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)。 注意:講述HT...
    kismetajun閱讀 28,817評(píng)論 1 45
  • 問(wèn)答題47 /72 常見(jiàn)瀏覽器兼容性問(wèn)題與解決方案? 參考答案 (1)瀏覽器兼容問(wèn)題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 14,154評(píng)論 1 92
  • 清晨,行在上班的路上,這是一條漫長(zhǎng)的路。這是我經(jīng)常走的路,也是我將要走很長(zhǎng)一段時(shí)間的路!出家門(mén)時(shí),還是一片...
    道武先生閱讀 312評(píng)論 0 2

友情鏈接更多精彩內(nèi)容