- 閱讀時間 7 分鐘

跟著元素的能見度使用 Intersection Observer API

Javascript
Markdown logo

Javascript 中的 Intersection Observer API 允許在頁面或容器中跟蹤元素的能見度。

問題

滾動頁面時,我們瀏覽其內容(平常是垂直的)。

螢幕右側有一個捲軸,指示頁面中的進度。

網頁中,如果想要跟蹤某些元素的能見度,可以監聽 scroll 事件來反應頁面的滾動:

window.addEventListener("scroll", () => {
  const scrollPosition = window.scrollY;

  // 根據位置執行操作,
  // 根據能見度執行相關操作
});

這種方法可以用,但有什麼問題?

  • 事件監聽函式會在滾動時重複執行,可能會過載主執行緒
  • 為了判斷頁面上的元素是否可見,要先獲取其位置(例如使用 getBoundingClientRect()),再將其與頁面中的滾動位置比較,因此需要手動計算
  • 如果想要判斷頁面可滾動容器中的元素是否可見,也需要手動計算,基於父元素的位置

Intersection Observer

Intersection Observer API 自然地解決這些問題。

根元素開始,它會在交集區域觀察元素。

當任何被觀察的元素進入退出交集區域時,可以執行回呼函式。

根元素

建立 IntersectionObserver 實例時,可以傳遞選項。

選項中,可以指定根元素:root。如果未指定,則為整個頁面。它可以是任何可滾動的容器,其中想要監視某些元素的能見度。

const observer = new IntersectionObserver(
  callback, // 回呼函式
  {
    // 根元素
    root: document.getElementById("container"),
  },
);

回呼函式

建立 IntersectionObserver 實例時,註冊回呼函式。

此函式會在兩種情況下執行:

  • 實例建立時
  • 被觀察的元素進入或退出交集區域時

它需要兩個參數:進入或退出交集區域的元素,以及 IntersectionObserver 的實例:

const observer = new IntersectionObserver(
  (entries, observer) => {},
  // 選項...
);

entries 中有一個元素陣列,每個元素都是 IntersectionObserverEntry 類型。可以循環遍歷它們並讀取有用資訊,例如:

  • isIntersecting, 最方便:元素是否在交集區域?
  • intersectionRatio, 可有用:元素與根元素的交叉比例是多少?

觀察元素

觀察元素很簡單。建立 IntersectionObserver 實例後,呼叫 observe 函式並傳遞要觀察的元素:

observer.observe(element);

然後,當元素進入或退出交集區域時,回呼函式將被觸發。

交集區域

預設情況下,交集區域覆蓋整個根元素。當被觀察的元素接觸到這個區域時,它將被視為可見。

const observer = new IntersectionObserver(callback);
Bloc 1non visible
Bloc 2non visible
Bloc 3non visible
Bloc 4non visible
Bloc 5non visible
Bloc 6non visible

以上範例中,當元素進入區域時,它們被視為可見。因此看起來所有元素都是一直可見的。

rootMargin

可以通過傳遞選項來使交集區域更大或更小:rootMargin

rootMargin 選項接受與 CSS margin 屬性相同的語法:top right bottom left。因此可以指定一個到四個數值,以像素、百分比或兩者為單位。

看看一個使用 rootMargin-100px 的例子,它將交集區域減少一百像素:

const observer = new IntersectionObserver(callback, {
  rootMargin: "-100px",
});
INTERSECTION
Bloc 1non visible
Bloc 2non visible
Bloc 3non visible
Bloc 4non visible
Bloc 5non visible
Bloc 6non visible

嘗試使用以下滑桿。要減少交集區域,因此會用負數值:

-90px
INTERSECTION
Bloc 1non visible
Bloc 2non visible
Bloc 3non visible
Bloc 4non visible
Bloc 5non visible
Bloc 6non visible

threshold

threshold 選項允許添加閾值,從而將元素視為可見。數值可用多種格式化:

  • 01 之間的數值等同於 0%100% 之間的百分比。例如如果傳遞 0.8,則元素在至少 80% 的內容可見時被視為可見。

使用 0.8 的例子:

const observer = new IntersectionObserver(callback, {
  threshold: 0.8,
});
Bloc 1non visible
Bloc 2non visible
Bloc 3non visible
Bloc 4non visible
Bloc 5non visible
Bloc 6non visible
  • 01 之間的數值陣列,指示 IntersectionObserver 實例在目標的能見度百分比時執行回呼函式。

使用每次 10% 的呼叫,顯示元素的能見度百分比(intersectionRatio):

const observer = new IntersectionObserver(callback, {
  threshold: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
});
Bloc 1non visible
Bloc 2non visible
Bloc 3non visible
Bloc 4non visible
Bloc 5non visible
Bloc 6non visible
Output :

處理表目錄

Intersection Observer API 使用的一個例子是處理表目錄。

閱讀文章時,當前部分被突出顯示。

就是 Astro Starlight 所做的:當段落標題進入交集區域時,相應的表目錄元素被突出顯示。

Astro Starlight 包含一個 web component。此組件包含 IntersectionObserver 實例的建立

也會看到交集區域計算,使用 rootMargin 選項:獲取導航欄的高度,添加移動設備上表目錄的高度,得到交集區域的開始。53 像素後就是區域的結束。當標題穿越這個區域時,相應的表目錄元素將改變其外觀。

其實 Astro Starlight 定義了兩個 web components:一個用於 移動設備(顯示在螢幕頂部),另一個用於較大螢幕(顯示在右側)。