IntersectionObserver'即將進入美景

IntersectionObserver 可讓您知道觀察的元素何時進入或離開瀏覽器的可視區域。

Browser Support

  • Chrome: 51.
  • Edge: 15.
  • Firefox: 55.
  • Safari: 12.1.

Source

假設您想追蹤 DOM 中的元素何時進入可見的可視區域。您可能需要這麼做,才能及時延遲載入圖片,或需要知道使用者是否確實正在查看特定廣告橫幅。方法是連結捲動事件,或使用週期性計時器,並在該元素上呼叫 getBoundingClientRect()。

不過,這種做法非常緩慢,因為每次呼叫 getBoundingClientRect() 都會強制瀏覽器重新配置整個網頁,並在網站中引入大量卡頓。如果網站是在 iframe 內載入,而您想知道使用者何時能看到某個元素,這幾乎是不可能的任務。單一來源模型和瀏覽器不允許您存取包含 iframe 的網頁資料。這類問題常見於廣告,例如經常使用 iframe 載入的廣告。

IntersectionObserver 的設計目的是提高這項可見度測試的效率,且已在所有新式瀏覽器中推出。IntersectionObserver 可讓您知道觀察到的元素何時進入或離開瀏覽器的可視區域。

iframe 顯示設定

如何建立IntersectionObserver

這個 API 相當小,最好透過範例說明:

const io = new IntersectionObserver(entries => {
  console.log(entries);
}, {
  /* Using default options. Details below */
});

// Start observing an element
io.observe(element);

// Stop observing an element
// io.unobserve(element);

// Disable entire IntersectionObserver
// io.disconnect();

使用 IntersectionObserver 的預設選項時,當元素部分進入檢視畫面,以及完全離開可視區域時,系統都會呼叫回呼。

如果您「需要」觀察多個元素,建議您使用「相同」IntersectionObserver 執行個體,多次呼叫 observe() 來觀察多個元素。

entries 參數會傳遞至回呼,也就是 IntersectionObserverEntry 物件陣列。每個物件都包含一個觀察元素的更新交集資料。

🔽[IntersectionObserverEntry]
    time: 3893.92
    🔽rootBounds: ClientRect
        bottom: 920
        height: 1024
        left: 0
        right: 1024
        top: 0
        width: 920
    🔽boundingClientRect: ClientRect
    // ...
    🔽intersectionRect: ClientRect
    // ...
    intersectionRatio: 0.54
    🔽target: div#observee
    // ...

rootBounds 是在根元素上呼叫 getBoundingClientRect() 的結果,預設為可視區域。boundingClientRect 是在觀察元素上呼叫 getBoundingClientRect() 的結果。intersectionRect 是這兩個矩形的交集,可有效指出觀察元素的「哪個部分」可見。intersectionRatio 與上述指標密切相關,可指出元素的可見程度。有了這項資訊,您就能在素材資源顯示在畫面上之前,實作即時載入等功能。有效率。

交集比率。

IntersectionObserver 會以非同步方式傳送資料,而回呼程式碼會在主執行緒中執行。此外,規格實際上指出 IntersectionObserver 實作應使用 requestIdleCallback()。這表示對您提供的回呼呼叫優先順序較低,瀏覽器會在閒置時間執行。這是刻意採用的設計。

捲動 div

我不太喜歡在元素內捲動,但我不是來評斷的,IntersectionObserver 也不是。options 物件會採用 root 選項,讓您定義可視區域的替代項目做為根層級。請務必注意,root 必須是所有觀測元素的前身。

交集所有項目!

才不是!開發人員太不小心了!這並非使用使用者 CPU 週期的明智做法。以無限捲動器為例:在這種情況下,建議您在 DOM 中加入哨兵,並觀察 (和回收!) 這些哨兵。您應該在無限捲動器中,靠近最後一個項目的位置新增一個標記。當該標記進入檢視畫面時,您可以使用回呼載入資料、建立下一個項目、將其附加至 DOM,並據此重新定位標記。如果正確回收 sentinel,就不需要額外呼叫 observe()。IntersectionObserver會繼續運作。

無限捲動

請提供更多更新

如前所述,當觀察元素部分進入檢視區塊時,回呼會觸發一次,而當元素離開檢視區塊時,回呼會再次觸發。這樣一來,IntersectionObserver 就能回答「元素 X 是否在檢視畫面中?」這個問題。不過,在某些用途中,這可能不夠。

這時 threshold 選項就能派上用場。可讓您定義 intersectionRatio 門檻陣列。每當 intersectionRatio 超過其中一個值時,系統就會呼叫回呼。threshold 的預設值為 [0],這說明瞭預設行為。如果將 threshold 變更為 [0, 0.25, 0.5, 0.75, 1],則每當元素多顯示四分之一時,我們就會收到通知:

門檻動畫。

還有其他選項嗎?

目前除了上述選項外,只有一個額外選項。rootMargin 可讓您指定根層級的邊界,有效擴大或縮小用於交集的區域。這些邊界是使用 CSS 樣式字串指定,例如 "10px 20px 30px 40px",分別指定頂端、右側、底部和左側邊界。總結來說,IntersectionObserver 選項結構體提供下列選項:

new IntersectionObserver(entries => {/* … */}, {
  // The root to use for intersection.
  // If not provided, use the top-level document's viewport.
  root: null,
  // Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.
  // If an explicit root element is specified, components may be percentages of the
  // root element size.  If no explicit root element is specified, using a
  // percentage is an error.
  rootMargin: "0px",
  // Threshold(s) at which to trigger callback, specified as a ratio, or list of
  // ratios, of (visible area / total area) of the observed element (hence all
  // entries must be in the range [0, 1]).  Callback will be invoked when the
  // visible ratio of the observed element crosses a threshold in the list.
  threshold: [0],
});

<iframe> magic

IntersectionObserver 是專為廣告服務和社群網路小工具設計,這些服務和小工具經常使用 <iframe> 元素,且可透過瞭解是否在檢視畫面中受益。如果 <iframe> 觀察到其中一個元素,則捲動 <iframe> 以及捲動 包含 <iframe> 的視窗,都會在適當的時間觸發回呼。不過,在後者的情況下,rootBounds 會設為 null,以免資料跨來源外洩。

IntersectionObserver「不」包含哪些內容?

請注意,IntersectionObserver 刻意不追求完美像素或低延遲。如果使用這些方法來實作捲動相關動畫等作業,一定會失敗,因為嚴格來說,資料在使用時已經過時。如要進一步瞭解 IntersectionObserver 的原始用途,請參閱說明文件。

我可以在回呼中執行多少工作?

簡短:在回呼中花費太多時間會導致應用程式延遲,所有常見做法都適用。

開始交集元素

IntersectionObserver 的瀏覽器支援良好,因為所有新式瀏覽器都支援。如有需要,您可以在舊版瀏覽器中使用 Polyfill,並在 WICG 的存放區中取得。顯然,使用該 polyfill 不會獲得原生實作帶來的效能優勢。

你可以立即開始使用 IntersectionObserver!請告訴我們你的想法。