Intersection Observer 簡介

Intersection Observer API提供了一種異步觀察目標元素與祖先元素或頂級文檔viewport的交集中的變化的方法。這使得以往較難實現的功能,更加簡單,例如,監聽圖片元素,在適當的時候懶加載圖片。

例子

先看下,下面是一個簡單的小例子

<div id="e1" style="background-color:black;width:100%;height:500px;">
  <ul style="color:white;">
    <li>boundingClientRect:<span id="el-boundingClientRect"></span></li>
    <li>intersectionRatio:<span id="el-intersectionRatio"></span></li>
    <li>intersectionRect:<span id="el-intersectionRect"></span></li>
    <li>isIntersecting:<span id="el-isIntersecting"></span></li>
    <li>rootBounds:<span id="el-rootBounds"></span></li>
    <li>time:<span id="el-time"></span></li>
  </ul>
</div>
var observer = new IntersectionObserver((entries,observer) => {
  // 我只監聽了一個對象
  let entry = entries[0]
  document.querySelector("#el-boundingClientRect").innerHTML = JSON.stringify(entry.boundingClientRect);
  document.querySelector("#el-intersectionRatio").innerHTML = JSON.stringify(entry.intersectionRatio);
  document.querySelector("#el-intersectionRect").innerHTML = JSON.stringify(entry.intersectionRect);
  document.querySelector("#el-isIntersecting").innerHTML = JSON.stringify(entry.isIntersecting);
  document.querySelector("#el-rootBounds").innerHTML = JSON.stringify(entry.rootBounds);
  document.querySelector("#el-time").innerHTML = JSON.stringify(entry.time);
  //document.querySelector("el-target").innerHTML = entry.target;
}, {
  threshold : [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
});
observer.observe(document.querySelector("#e1"));

簡書上由于安全原因,不能植入js腳本,所以無法預覽,如果你希望預覽例子,請前往我的博客

構造器

通過例子,我們可以看到Intersection Observer需要通過構造器來創建,即new IntersectionObserver(callback[, options]),參數有兩部分組成,一個必傳的回調函數以及一個可選的配置參數

回調Callback

Intersection Observer的翻譯就是“交點觀察”,因此,他的回調就成了重點。當觀察元素與根元素之間的交叉狀態發生變化時,它會將這部分信息反饋回來--通過回調告知

在回調函數里,我們會接受到兩個對象,發生狀態變化的元素集合以及監聽者本身(注意:創建時,會將所有被觀察元素的狀態傳遞過來),監聽者本身我們可以通過它增加或者減少監聽的元素或者銷毀自身(后續講),這里我們更關注觀察元素

function callback(entries,observer) {
  entries -> 一系列被觀察的元素
  observer -> 觀察者
}

entries 是一個 IntersectionObserverEntry 對象的數組,IntersectionObserverEntry 包換以下元素(來自MDN)

  • boundingClientRect: 返回包含目標元素的邊界信息的DOMRectReadOnly. 邊界的計算方式與 Element.getBoundingClientRect() 相同
  • intersectionRatio: 返回intersectionRect 與 boundingClientRect 的比例值
  • intersectionRect: 返回一個 DOMRectReadOnly 用來描述根和目標元素的相交區域
  • isIntersecting: 返回一個布爾值, 如果目標元素與交叉區域觀察者對象(intersection observer) 的根相交,則返回 true .如果返回 true, 則 IntersectionObserverEntry 描述了變換到交叉時的狀態; 如果返回 false, 那么可以由此判斷,變換是從交叉狀態到非交叉狀態
  • rootBounds: 返回一個 DOMRectReadOnly 用來描述交叉區域觀察者(intersection observer)中的根
  • target: 與根出現相交區域改變的元素 (Element)
  • time: 返回一個記錄從 IntersectionObserver 的時間原點(time origin)到交叉被觸發的時間的時間戳(DOMHighResTimeStamp)

具體的值是多少,我們可以在最上面的例子中看到,需要注意傳遞過來的對象都是只讀(畢竟回調,只是通知你發生變化了)

參數options

我們可以通過 options 配置 IntersectionObserver,他包含以下幾項配置

  • root: 監聽元素的祖先元素Element對象,其邊界盒將被視作視口。目標在根的可見區域的的任何不可見部分都會被視為不可見。默認情況下文檔視口會作為root
  • rootMargin: 一個在計算交叉值時添加至根的邊界盒(bounding_box)中的一組偏移量,類型為字符串(string) ,可以有效的縮小或擴大根的判定范圍從而滿足計算需要。語法大致和CSS 中的margin 屬性等同。默認值是"0px 0px 0px 0px"。
  • threshold: 規定了一個監聽目標與邊界盒交叉區域的比例值,可以是一個具體的數值或是一組0.0到1.0之間的數組。若指定值為0.0,則意味著監聽元素即使與根有1像素交叉,此元素也會被視為可見. 若指定值為1.0,則意味著整個元素都交叉時視為可見。閾值的默認值為0.0。

在上面的例子中,我未修改root與rootMargin,你可以將瀏覽器的窗口作為可見區域,threshold定義了一系列數組,意味著到達那些交叉比時觸發回調

屬性

在創建或者回調函數中,我們可以得到 IntersectionObserver 對象,他包含以下屬性:

  • root: 所監聽對象的具體祖先元素(element)。如果未傳入值或值為null,則默認使用頂級文檔的視窗
  • rootMargin: 計算交叉時添加到根(root)邊界盒bounding box的矩形偏移量, 可以有效的縮小或擴大根的判定范圍從而滿足計算需要。此屬性返回的值可能與調用構造函數時指定的值不同,因此可能需要更改該值,以匹配內部要求。所有的偏移量均可用像素(pixel)(px)或百分比(percentage)(%)來表達, 默認值為"0px 0px 0px 0px"
  • thresholds: 一個包含閾值的列表, 按升序排列, 列表中的每個閾值都是監聽對象的交叉區域與邊界區域的比率。當監聽對象的任何閾值被越過時,都會生成一個通知(Notification)。如果構造器未傳入值, 則默認值為0

IntersectionObserver 的屬性也都是只讀,他在創建之后不支持修改

方法

IntersectionObserver 通過以下方法添加或者取消監聽元素

  • IntersectionObserver.disconnect() 使IntersectionObserver對象停止監聽工作。
  • IntersectionObserver.observe() 使IntersectionObserver開始監聽一個目標元素。
  • IntersectionObserver.takeRecords() 返回所有觀察目標的IntersectionObserverEntry對象數組。
  • IntersectionObserver.unobserve() 使IntersectionObserver停止監聽特定目標元素。

真實的例子

簡書上由于安全原因,不能植入js腳本,所以無法預覽,如果你希望預覽例子,請前往我的博客

懶加載

IntersectionObserver 可以很簡單的實現圖片懶加載,開頭就提到的,看下面很少的代碼,我們就實現了懶加載的功能(目前已經好像圖片懶加載框架使用它了,比如lozad.js)

<img id="e2-lazy" load-url="/images/avatar.jpg" />
var observer = new IntersectionObserver((entries,observer) => {
  let entry = entries[0]
  if (entry.isIntersecting) {
    let el = entry.target;
    el.src = el.getAttribute('load-url');
  }
});
observer.observe(document.querySelector("#e2-lazy"));

TOC 自動定位

我的博客(Cake與NexT主題)的側邊欄,它使用的就是IntersectionObserver。不過在使用時,也遇到了些問題。比如,IntersectionObserver 在瀏覽器中的優先級不高(高效,必然也有缺陷),如果你很快的移動,可能無法觸發

我們(NexT團隊)第一次嘗試時,并未考慮優先級的事情,我們根據標題元素的是否在瀏覽器窗口可見計算其toc的位置,但如果快速滑動,那么toc的定位就會錯亂

后續,我們調整,擴大根窗口的區域(與文章一樣長)的方式,那么我們只需要判斷是不是已經交叉即可,即便滑動太快也無需擔心

相關的代碼可以見:

本文作者: Mr.J
本文鏈接: https://www.dnocm.com/articles/beechnut/intersection-observer-info/
版權聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協議。轉載請注明出處!

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容