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 選項,讓您將 viewport 以外的項目定義為根目錄。請務必注意,root 必須是所有觀察元素的祖系。

交集所有事物!

不可以!開發人員不佳!這並非明智的使用者 CPU 週期用法。以無限捲動器為例:在這種情況下,建議您在 DOM 中新增哨兵,並觀察 (和回收) 這些哨兵。您應在無限捲動器中,靠近最後一個項目的位置新增哨兵。當該哨兵進入檢視範圍時,您可以使用回呼來載入資料、建立下一個項目、將這些項目附加至 DOM,並據此重新定位哨兵。如果您妥善回收哨兵,就不需要額外呼叫 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> 魔術

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

IntersectionObserver 不是什麼?

請注意,IntersectionObserver 並未刻意追求像素完美或低延遲。使用這些方法實作捲動依附動畫等功能,必定會失敗,因為在您使用這些方法時,資料將會過時。說明文件中進一步說明 IntersectionObserver 的原始用途。

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

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

繼續前進,並交疊元素

IntersectionObserver 的瀏覽器支援度很高,因為它可用於所有新式瀏覽器。如有需要,您可以在舊版瀏覽器中使用 polyfill,這些 polyfill 可在 WICG 存放區中取得。顯然,使用原生實作方式,您就不會獲得使用該 polyfill 的效能優勢。

您現在就可以開始使用 IntersectionObserver!請告訴我們你想出什麼解決方法。