原文:www.hweaver.com/intersectio…javascript
現代網站重度依賴 scroll 事件並非什麼祕密了。滾動能夠觸發圖片懶加載或延遲請求數據、初始化動畫、支持無盡內容的加載,如此等等。糟糕的是這些 scroll 事件都不太可靠,也都是資源消耗大戶。這在實現效果方面引發了問題,也經常讓瀏覽器不堪重負。html
做爲一種處理 scroll 事件的新方式,交集觀察者(Intersection Observer API)
應運而生。該 API 容許用戶觀察指定元素 A,並監視其與特定元素 B (或瀏覽器視口)的 交集(intersection) 狀態。前端
既有的實現究竟有何問題?考慮一個當下典型的站點頁面,有不少 scroll 事件在發生 -- 廣告模塊、從底部滾動進來的新內容、時不時須要運行動畫的元素,或是頁面中的不少圖片,都會滾動至被用戶看到後纔會加載或執行。這些 scroll 事件關聯了無數的循環調用方法,其中不乏好比須要得到必要位置信息的 Element.getBoundingClientRect()
等等,都是性能敏感的。java
這些方法都運行在主線程中,這意味着一個地方出現問題就會殃及全部事情。Intersection Observer API 讓瀏覽器免於應付交集事件,經過使用關聯特定元素的交集狀態的回調函數取而代之。瀏覽器能夠更有效地管理這些事件,性能也獲得了優化。git
須要注意的是瀏覽器兼容性,截至本文被翻譯時的統計以下:github
爲了徹底理解爲什麼 Intersection Observer API 更益於性能,先來看看基礎知識。數組
定義一個 Intersection Observer 實例時,有一些關鍵的術語。瀏覽器
根(root) 指的是等待一個對象與其產生交集的某個元素。默認來講,就是瀏覽器視口(viewport),但任何合法的元素都是可使用的。前端優化
除了以 root 做爲一個單獨 IntersectionObserver
的基礎,觀察者還能夠監視許多不一樣的 目標(target)。目標也多是任意合法的元素,當任何一個目標和根元素髮生交集時,觀察者會觸發一個回調函數。函數
創建一個簡單的 IntersectionObserver
很是方便。首先調用 IntersectionObserver
構造器,並向其傳入一個回調函數和一個預設的選項:
const options = {
root: document.querySelector('#viewport'),
rootMargin: '0px',
threshold: 1.0
};
const observer = new IntersectionObserver(callback, options);
複製代碼
如上所示,選項中有一些可用的屬性:
root
用來檢查是否和目標元素髮生交集的根元素。該選項接受任何合法元素,可是根元素必須是目標元素的祖先,這一點很重要。若是不指定根元素,或設爲 null
,則瀏覽器視口就做爲默認的根元素。
rootMargin
該屬性被用來擴展或縮減根元素的尺寸。接受和 CSS 中的 margin 相同格式的值,好比一個單獨的值 10px
或定義不一樣邊的多個值 10px 11% -10px 25px
。
threshold
最後,threshold(譯註:閾yù值)選項指定了一個最小量,表示目標元素和根元素交集時,其自身知足該最小量纔會觸發回調。取值爲 0.0 – 1.0 之間的一個浮點數,因此 75% 左右的交集率應該寫成 0.75。若是但願在多個點觸發回調,也能夠傳入一個值的數組,如 [0.33, 0.66, 1.0]
。
一旦 IntersectionObserver
實例被建立,剩下所要作的就是提供一個或多個目標元素以供觀察:
const target = document.querySelector('#target');
observer.observe(target);
複製代碼
今後,回調函數將會在目標(或多目標)接近交集閾值的時刻被觸發。
const callback = function(entries, observer) {
entries.forEach((entry) => {
// 在這乾點什麼
});
}
複製代碼
理解交集如何計算是重要的。首先,Intersection Observer API 將任意物體都視爲矩形以便計算。這些矩形在包含目標內容的前提下,將被儘量小的計算。
對於根元素,基於 rootMargin
的值考慮其矩形邊界,這個值會填充或減少根元素的尺寸。
最後相當重要的是,要理解不一樣於傳統 scroll 事件的是,Intersection Observer 並非在每次交集改變後不間斷地輪詢。相反,回調只在閾值大約達到時被調用。若是須要屢次檢測,提供多個閾值就好了。
在第一個小項目中,咱們用一種簡單的方式來看看 Intersection Observer API。
// 動畫回調函數
const scrollImations = (entries, observer) => {
entries.forEach((entry) => {
console.log(111, entry.isIntersecting, entry.intersectionRatio);
if(entry.isIntersecting && entry.intersectionRatio >= 1) {
// 徹底看到元素時
entry.target.classList.add('box--visible');
} else {
entry.target.classList.remove('box--visible');
}
});
}
// 建立觀察者
const options = {
threshold: 1.0,
};
const observer = new IntersectionObserver(scrollImations, options);
// 指定觀察目標
const boxes = document.querySelectorAll('.box');
boxes.forEach((box) => {
observer.observe(box);
});
複製代碼
向下滾動,一系列元素會出現。用一個 IntersectionObserver
實例監視 3 個目標元素。當它們徹底進入視口(root)後,向目標元素上附加一個樣式類名,觸發對應的 CSS 動畫。
對於單頁中隨着滾動、相應某個區域的出現而高亮的導航條,Intersection Observer 是很適用的。
// 初始化觀察者
const options = {
threshold: 0.45
}
const observer = new IntersectionObserver(changeNav, options);
// 指定目標元素
const sections = document.querySelectorAll('section');
sections.forEach((section) => {
observer.observe(section);
});
複製代碼
changeNav()
回調函數簡單的檢查目標 section 元素是否足夠多的出如今屏幕上,而後恰當地指定樣式類名。
const changeNav = (entries, observer) => {
entries.forEach((entry) => {
// 檢查元素髮生了碰撞
if(entry.isIntersecting && entry.intersectionRatio >= 0.55) {
// 刪除舊的 active 樣式類
document.querySelector('.active').classList.remove('active');
// 取得知足條件的目標元素 id
var id = entry.target.getAttribute('id');
// 找到匹配的元素並添加類名
var newLink = document.querySelector(`[href="#${id}"]`).classList.add('active');
}
});
}
複製代碼
對於尚不支持該特性的瀏覽器,有 polyfill 很好的填補了空白。官方的代碼和文檔能夠在這裏找到: github.com/w3c/Interse…
更簡單的方法是適用 polyfill.io (polyfill.io)。能夠單獨指定須要加載的 Polyfill,且知足條件的瀏覽器纔會加載。這能夠保證頁面的輕量,同時又不用過多配置。其用法以下:
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
複製代碼
一旦 polyfill 被加載,以上 demos 就能在 Safari、IE7+ 等瀏覽器上運行了。
如你所見,Intersection Observer API 簡單易用又富創造性。儘管可能須要 polyfill,但瀏覽器支持也在持續改善。該 API 將成爲前端優化的利器。
搜索 fewelife 關注公衆號
轉載請註明出處