JavaScript Intersection Observer 交叉觀察器
在過去,要判斷一個網頁元素是否出現在使用者的螢幕視窗(Viewport)內,通常需要監聽 scroll 事件並頻繁調用 getBoundingClientRect()。這種做法不僅耗費性能,還容易造成頁面捲動卡頓(Jank)。
Intersection Observer API 提供了一種更高效、非同步的方式來監測一個目標元素是否進入或離開了另一個元素(通常是瀏覽器視窗)。
為什麼要使用 Intersection Observer?
- 效能卓越: 觀察動作是在瀏覽器後台異步執行的,不會阻塞主執行緒。
- 減少計算: 瀏覽器會優化觸發時機,只有在「交叉狀態」發生變化時才執行回呼函式。
- 語法簡潔: 代替了複雜的偏移量計算邏輯。
基本語法
建立一個觀察器需要兩個部分:回呼函式 (callback) 和 設定選項 (options)。
// 1. 設定選項
const options = {
root: null, // 預設為 null,代表瀏覽器視窗 (viewport)
rootMargin: '0px', // 延伸或收縮 root 的邊界 (類似 CSS margin)
threshold: 0.1, // 觸發門檻,0.1 代表元素出現 10% 時觸發
};
// 2. 建立回呼函式
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log('元素進入視窗!', entry.target);
// 可以在這裡停止觀察該元素
// observer.unobserve(entry.target);
}
});
};
// 3. 實例化觀察器並開始觀察
const observer = new IntersectionObserver(callback, options);
const target = document.querySelector('#targetElement');
observer.observe(target);
核心設定參數說明
root: 指定用來觀察的容器元素。如果不指定,則預設為瀏覽器的視窗。rootMargin: 可以用來提前觸發事件。例如設定為'200px',則元素距離進入視窗還有 200px 時就會被視為「已進入」。這對圖片預載非常有幫助。threshold: 一個數字或陣列。設定為[0, 0.5, 1]代表元素在出現 0%、50%、100% 時都會各觸發一次。
實戰範例:圖片懶加載 (Lazy Loading)
這是 Intersection Observer 最常見的用途。我們可以先將真實圖片路徑存在 data-src 屬性中,等圖片出現在螢幕內時再放入 src。
const lazyImages = document.querySelectorAll('img.lazy');
const imageObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // 將暫存路徑填入真實 src
img.classList.remove('lazy');
observer.unobserve(img); // 載入後取消觀察,節省資源
}
});
},
{ rootMargin: '100px' }
); // 提早 100px 載入,讓使用者感覺不到在載入
lazyImages.forEach((img) => imageObserver.observe(img));
實戰範例:無限捲動 (Infinite Scroll)
在列表的最下方放一個小小的偵測元素(例如 ID 為 sentinel 的 div),當它進入視窗時就向後端抓取新資料。
const sentinel = document.querySelector('#sentinel');
const scrollObserver = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadMoreContent(); // 執行載入更多內容的函式
}
});
scrollObserver.observe(sentinel);
注意事項
- 瀏覽器支援度: 現代瀏覽器(Chrome, Firefox, Safari, Edge)皆完整支援。若需支援極舊版瀏覽器,可使用 W3C 提供的 Polyfill。
- 停止觀察: 為了維持最佳性能,當目標元素的任務完成(如圖片已載入)後,務必調用
unobserve(element)或disconnect()。
透過 Intersection Observer,你可以用極低的效能代價實現流暢、現代化的使用者互動介面。
相關閱讀: