IntersectionObserver'即將進入美景

IntersectionObservers 會通知你,觀察到的元素進入或離開瀏覽器的可視區域。

瀏覽器支援

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

資料來源

假設您想在 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 的預設選項,當元素有部分進入檢視畫面及完全離開可視區域時,系統就會呼叫回呼。

如果您「需要」觀察多個元素,則可以多次呼叫 observe(),以觀察使用同一個 IntersectionObserver 例項的多個元素。

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

我不喜歡捲動元素內部,但我不是來評斷這些元素,也不是 IntersectionObserveroptions 物件採用 root 選項,可讓您將可視區域的替代方案定義為根層級。請注意,root 必須是所有觀測元素的祖系。

與所有東西相交!

不!開發人員錯誤!別考慮到使用者的 CPU 週期用量。以下以無限捲動器為例:在這種情況下,建議將 sentinels 新增至 DOM 中,然後觀察 (並回收) 這些元素。您應該在無限捲軸中,為最後一個項目新增靠近最後一個項目。進入檢視畫面後,您可以使用回呼來載入資料、建立下一個項目、將其附加至 DOM,然後根據位置重新調整其位置。如果你已妥善回收密件,就不需要額外呼叫 observe()IntersectionObserver 會繼續運作。

無限捲軸

敬請期待更多新消息

如前所述,當觀察到的元素有部分進入檢視區塊時,只會觸發一次回呼,而在其離開可視區域時則會觸發一次回呼。這樣一來,IntersectionObserver 即可回答「X 元素顯示在檢視畫面中嗎?」這個問題。但在某些用途中,這個金額可能不敷使用。

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

門檻動畫。

還有其他選擇嗎?

目前上述選項還只有一個額外選項。rootMargin 可讓您指定根的邊界,有效讓您擴大或縮小十字路口所用的區域。這些邊界是使用 CSS 樣式字串 á la "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,而且可在 WICG 的存放區中找到。您顯然無法使用原生實作 Polyfill 帶來的效能優勢。

你現在可以開始使用「IntersectionObserver」了!請告訴我們你的想法。