最近在做無限滾動(Infinite Scroll)的時候發現了 IntersectionObserver
這個好東西。以往在使用上要計算高度、檢查元素是否進入視窗等,使用這個 Web API 能夠讓我們更方便的去達成、同時效能也更好。
因為接下來的文章的是以筆記型式撰寫,詳細的教學請參考這兩篇:
使用 new
來建立 IntersectionObserver
,並帶入兩個參數:callback
和 options
。
let observer = new IntersectionObserver(callback, options);
首先看看參數 options
:
options
這個參數又可以設定三個屬性,分別是:
null
(以瀏覽器的 viewport 為範圍)。[0, 0.2, 0.6, 1]
。const opition = {
root : null,
rootMargin: "0px 0px 0px 0px",
threshold: 0
};
callback
接受兩個參數,分別是 entries
和 observer
:
// 取得 DOM
const imgs = document.querySelectorAll('img')
// 建立 IntersectionObserver
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
// ...
})
}, opition)
// 監聽每個 img
imgs.forEach(img => observer.observe(img))
entries
的清單,包含了各個物件中的資訊。以下為 console.log(entries)
的結果。observer
本身,進行停止觀察等行為。將剛剛 console.log(entries)
其中的內容展開後可以發現幾個實用的方法。
entry.isIntersecting
:確認元素是否進入 viewport,回傳 true
或 false
。entry.intersectionRect
:被觀察對象的左上角在 viewport 的距離。entry.intersectionRatio
:元素有多少比例在 viewport 中(0 到 1)。entry.target
:元素本身。上面的程式碼使用 observe()
來觀察,相反的如果已達成條件、不需要再觀察的話,使用 unobserve
即可。
observer.unobserve(element);
這邊簡單製作一個不斷載入狗圖片的程式,首先 HTML 長這樣:
<ul>
<li><img src="./images/dog.jpg" alt="狗圖"></li>
<li><img src="./images/dog.jpg" alt="狗圖"></li>
<li><img src="./images/dog.jpg" alt="狗圖"></li>
<li><img src="./images/dog.jpg" alt="狗圖"></li>
<li><img src="./images/dog.jpg" alt="狗圖"></li>
<li><img src="./images/dog.jpg" alt="狗圖"></li>
</ul>
ul
中放了一堆 li
,之後的過程只要不斷增加 li
就好。這邊提供兩個方式去實作:
【方法一】 在底部增加一個元素,判斷這個元素是否進入視窗。
【方法二】 不斷去抓最後一個 li
的 DOM,再觀察這個 DOM 是否進入視窗。
我採用方法一來實作,直接在 HTML 底下增加一個 <div id="obj"></div>
。
// 以上略 ...
<li><img src="./images/dog.jpg" alt="狗圖"></li>
<li><img src="./images/dog.jpg" alt="狗圖"></li>
</ul>
// 觀察這個 div
<div id="obj"></div>
接著是 JavaScript,我們針對 #obj
來監聽,看它是不是進入視窗,如果進入的話直接增加一個 li
,範例如下:
const list = document.querySelector("ul");
const obj = document.querySelector('#obj');
const opition = {
root: null,
rootMargin: "0px 0px 0px 0px",
threshold: 0
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 當 obj 進入視窗後,增加 li
list.innerHTML += '<li><img src="./images/dog.jpg" alt="狗圖"></li>'
}
})
}, opition);
// 監聽 obj
observer.observe(obj);
可以發現只要滑到最底下就會再新增一個 li
。
這邊簡易示範了無限載入,如果是要接 API 的資料、或是想增加 CSS 動畫等需求的話,再請各位自行修改囉。