IntersectionObserver:上篇-基本介紹及使用

IntersectionObserver:上篇-基本介紹及使用

IntersectionObserver?拿超商的歡迎光臨來說明

一開始知道有「IntersectionObserver」這個Web API,是在一篇講圖片lazy load的文章上看到的。

以前我們要做圖片的延遲載入(lazy load),會用onscroll來監聽圖片那個標籤的offset().Top是不是怎麼樣又怎麼樣的計算後,確定圖片出現在視窗上了,才把圖片的路徑塞進src裡,以前Augustus就寫過這樣:

$('selector').offset().top - $(window).scrollTop() <= $(window).height()

這個的缺點就是onscroll,瀏覽器會在每一次的捲動都執行監聽的動作。

IntersectionObserver這個API,很純悴的就是指定的目標出現在觀察器(window)中時,就傳一個true來告知。

onscroll跟IntersectionObserver有什麼不一樣呢?我們來用便利商店來舉例。

假設今天你是員工,老闆要求只要有客人進來,你都要喊「歡迎光臨」。

onscroll指的就是,從此後你就一直盯著門口,確認路人A進來時就喊歡迎光臨。這樣沒什麼不對,你確實達成目標了,但你也要花心力一直盯著門口看,做其它事情也心驚膽跳的怕有人來。

那IntersectionObserver是什麼呢?就是自動門,有人進來時它就發出「叮咚(true)」告訴你有人進來了。那是不是你就可以安心的做其它事,自動門發出聲音時再喊就好?

換成網頁的角度來看,「你」就是瀏覽器,「客人」就是要延遲載入的img,那只要聽到IntersectionObserver發出叮咚了,再來執行img src放上正確路徑這個函式就行。

本篇會筆記的分成上、下2篇。

上篇,就是本篇,是Augustus用自己的方式理解IntersectionObserver的使用方式,以及相關參數說明。

下篇,預計提供幾個實用的範例,像是lazy load、無限滾動、側邊欄固定等。

另外為了不讓IntersectionObserver這麼長的名稱干擾閱讀,以下簡稱「IO」


IO使用3步驟

使用IO的步驟很簡單,就3步,如下:

  1. 建立觀察器(observer),觀察器要有個鏡頭來觀察,這個鏡頭就是 root,root如果沒有指定,預設就是指 window
  2. 指定觀察器要觀察的獵物(entry)
  3. 當獵物 進入(true) / 離開(false) 觀察器視窗的範圍,觀察器就執行callback

在寫這篇前看了幾篇教學文(推薦的三篇會附在文未),因為寫的是高手,所以沒有像Augustus一樣需要一個完整的使用步驟來理解及記憶,痛定思痛,不希望看到這篇的你對IO一頭霧水,就先整理出這三個步驟,以下開始筆記這三個步驟的程式碼。


1 建立觀察器(observer)

建立觀察器很簡單,就一行:

var observer = new IntersectionObserver(callback, [option]);

callback就是當獵物(entry)進入到觀察器的鏡頭(root)內時,要做什麼事的function。

option是選填,不填就是預設值,這一段Augustus是直接用一個console.log來看會log出什麼來理解的,看到console.log出option的東西後就懂了:

option就是調整觀察器的鏡頭用的。

option總共有以下幾個值可以填:

{
  root: null,
  rootMargin: "0px 0px 0px 0px",
  threshold: [0]
}

root

這邊想像成觀察器的鏡頭比較好理解。當目標進入到鏡頭內就callback,離開了鏡頭也callback。root可以設一個element,比方寫:

root: document.getElementById('container');

root的預設是null,root為null時就代表鏡頭就是你的視窗,就是螢幕正在看的區域。

rootMargin

4個值分別代表上、右、下、左,這個是放大或縮小鏡頭的邊界用的。比方你的螢幕只有13寸,但你想讓鏡頭的範圍拉大到15寸,這樣就可以讓目標離不開你的視線,那就填rootMargin來拉大。

rootMargin的預設值是 "0px 0px 0px 0px"

2019.10.28補充:
rootMargin,實際寫Demo頁時,發現使用起來完全不是理解的這麼回事。
比方我想把鏡頭往下移2000px,寫了2000px 0px 0px 0px後,卻還是一樣目標一出現到視窗底部,就判斷entry.isIntersecting === true了。
google了一下,也有其他人有遇到這問題:IntersectionObserver rootMargin’s positive and negative values are not working,目前還沒看到有人回答。
所以關於rootMargin這點,如果有大大知道原因,敬請於回覆區中回覆。

threshold

指獵物本身出現了多少部份在你的鏡頭裡,而出現的部份到了指定的百分比後,都會執行callback。這個直接看圖比較好懂:

綠色的方塊出現在視窗中的範圍不同,上面那一排紅底的字就會顯示不同文字。

threshold的預設值是[0],就是緣色方塊一接觸到視窗的邊就callback,像上圖就是設成:[0, 0.25, 0.5, 0.75, 1],代表綠色方塊出現了0%、25%、50%、75%、100%這5個範圍後,都要執行一次callback。

再看一次建立觀察器的code:

var observer = new IntersectionObserver(callback, [option]);

option上面介紹完了,callback會寫在第3步,本段要再說明一小段,就是當observer這個變數被命出來後,代表觀察器建好了,它本身可以做3件事:

  1. 開始觀察某個獵物:observer.observe(el)
  2. 取消觀察某個獵物:observer.unobserve(el)
  3. 自爆,關掉這個觀察器:observer.disconnect()

2 指定觀察器要觀察的獵物(entry)

第一步把觀察器建出來,接著要開始找獵物來觀察。比方我們想觀察<img id="img">,就可以寫:

function callback(entry) {}

var observer = new IntersectionObserver(callback);

var img = document.getElementById('img');

observer.observe(img);

那當然,也可以直接用個迴圈觀察所有圖片:

function callback(entry) {}

var observer = new IntersectionObserver(callback);

var all_img = document.querySelectorAll('img');
Array.prototype.forEach.call(all_img, function(img) {
  observer.observe(img);
});

3 當獵物 進入(true) / 離開(false) 觀察器視窗的範圍,觀察器就執行callback

上面第2步的code可以看到, function callback(entry) 這邊,IO的callback會帶一個參數進來,常看到命名為entry或entries,之所以會用複數是因為這個參數會是一個陣列。

像第2步的code,有觀察1張圖片的,或是觀察多張圖片的,callback帶進來的都會是陣列,不管觀察的獵物是1個或多個。

我們來 console.log(entry) 看一下entry這個陣列有什麼:

[
    {
      // ReadOnly:目標元素的矩形區域的信息
      boundingClientRect: { /* ... */ },

      // 獵物的可見比例
      intersectionRatio: 1,

      // ReadOnly:獵物與root的交叉區域
      intersectionRect: { /* ... */ },

      // 是否出現在鏡頭(root))中
      isIntersecting: true,

      // ReadOnly:鏡頭(root)的資訊
      rootBounds: { /* ... */ },

      // 獵物本身
      target: 獵物的DOM節點
    }
  ]

這邊講3個比較常會用到的:intersectionRatio、isIntersecting、target。

intersectionRatio

獵物的可見比例,這跟IO的threshold很像,都是在看獵物出現在視窗中多少部份。不一樣的是IO是指定全部獵物,而intersectionRect是指能讀,不能改變,可以讀出每個獵物出現了多少部份,直接看圖比較好懂:

可以看到,鏡頭裡上面的出現了75%,中間的出現了100%,最下面的出現了12%。

target

target 就是這個獵物在DOM上的節點,所以可以直接用 entry[0].target 來抓到它。

isIntersecting

這個就是最重要的,就是超商那個例子的自動門「叮咚」聲。

當獵物進入到鏡頭後,isIntersecting會是true,不在鏡頭內就是false。

所以再拿圖片lazy load來當示範,就可以這樣寫:

function callback(entry) {
  if(entry[0].isIntersecting) {
    entry[0].target.src = entry[0].target.dataset.src;
    observer.unobserve(entry[0].target);
  }
}

var observer = new IntersectionObserver(callback);

var img = document.getElementById('img');

observer.observe(img);

entry[0].isIntersectingtrue 時就執行替換img src的動作,並且取消對這個獵物的觀察,這樣替換src的動作就只會執行這一次。


原始碼

IO的3個步驟筆記完了,IO也會使用了,這邊附上Augustus整理過的程式碼,忘記的話以後可以直接看原始碼來找:


參考資源

本篇筆記主要看了3篇的教學文跟文件:


本篇主要是IO的基本使用,下一篇會開始實作幾個範例。

IntersectionObserver:下篇-實際應用 lazyload、進場效果、無限捲動


Summary
IntersectionObserver:上篇-基本介紹及使用
Article Name
IntersectionObserver:上篇-基本介紹及使用
Description
本篇大綱:IntersectionObserver?拿超商的歡迎光臨來說明。IO使用3步驟:1 建立觀察器(observer)、2 指定觀察器要觀察的獵物(entry)、3 當獵物 進入(true) / 離開(false) 觀察器視窗的範圍,觀察器就執行callback。原始碼。參考資源。
Augustus
Let's Write
Let's Write
Publisher Logo

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *