概念html
IntersectionObserver
接口(從屬於Intersection Observer
API)爲開發者提供了一種能夠異步監聽目標元素與其祖先或視窗(viewport
)交叉狀態的手段。祖先元素與視窗(viewport
)被稱爲根(root
)。前端
功能npm
網頁開發時,經常須要判斷某個元素是否進入了"視口"(viewport),即用戶能不能看到它,而後執行相應的邏輯。數組
常見的方法是監聽scroll
事件,調用元素的getBoundingClientRect
方法,獲得它對應於視口左上角的座標,再判斷是否在視口以內。這種方法的缺點是,因爲scroll
事件密集發生,計算量很大,容易形成性能問題。bash
IntersectionObserver
是一個新的API,能夠自動觀察元素是否進入視口。因爲可見的本質是,目標元素與視口產生一個交叉區,因此這個API叫作「交叉觀察器」。異步
用法:函數
let io = new IntersectionObserver(callback, option);
// 開始觀察
io.observe(document.getElementById('example'));
// 中止觀察
io.unobserve(element);
// 關閉觀察器
io.disconnect();
複製代碼
callback性能
callback
方法是被監聽元素可見性發生時,執行的回調函數。通常會觸發兩次,一次是目標元素剛剛進入視口(開始可見),另外一次是徹底離開視口(開始不可見)。動畫
var io = new IntersectionObserver(
entries => {
console.log(entries);
}
);
複製代碼
entries
是一個數組,數組中的每個元素都是IntersectionObserverEntry
對象。特別注意,若是監聽的是10個元素,可是隻有2個元素可見性發生變化,數組entries
的長度就是2,存放的只是可見性發生變化的元素。ui
IntersectionObserverEntry對象
IntersectionObserverEntry
提供被觀察元素的信息,有如下七個屬性。
boundingClientRect
目標元素的矩形信息
intersectionRatio
相交區域和目標元素的比例值
intersectionRect/boundingClientRect
不可見時小於等於0
intersectionRect
目標元素和視窗(根)相交的矩形信息 能夠稱爲相交區域
isIntersecting
目標元素當前是否可見 Boolean
值 可見爲true
rootBounds
根元素的矩形信息,沒有指定根元素就是當前視窗的矩形信息
target
觀察的目標元素
time
返回一個記錄從IntersectionObserver
的時間到交叉被觸發的時間的時間戳
上面提到的回調執行兩次的問題能夠經過IntersectionObserverEntry
對象的isIntersecting
屬性解決,經過這個屬性判斷是不是開始可見。
option
配置對象
主要用於配置IntersectionObserver
的一些觀察行爲屬性,主要有如下屬性:
1. threshold
threshold屬性決定了何時觸發回調函數。它是一個數組,每一個成員都是一個門檻值,默認爲[0],即交叉比例(intersectionRatio)達到0時觸發回調函數。
new IntersectionObserver(
entries => {/* ... */},
{
threshold: [0.3, 0.5]
}
);
複製代碼
如代碼中所示,當目標元素30% 或者 50% 可見時,會觸發callback回調。
2. root 屬性,rootMargin 屬性
IntersectionObserver
不只支持視口內滾動,還支持容器內滾動。root
屬性指定目標元素所在的容器節點(即根元素)。注意,容器元素必須是目標元素的祖先節點。
rootMargin
屬性用來擴展或縮小rootBounds
這個矩形的大小,從而影響intersectionRect
交叉區域的大小。它使用CSS
的定義方法,好比10px 20px 30px 40px
,表示 top、right、bottom
和 left
四個方向的值。
var opts = {
root: document.querySelector('.container'),
rootMargin: "500px 0px"
};
var observer = new IntersectionObserver(
callback,
opts
);
複製代碼
基於這個API高性能的特性,小編在平常的業務中作了以下的實踐,使用效果很不錯,同時也簡化了業務實現邏輯。
效果以下圖:
具體實現代碼以下:
// index.html
<div class="default-labelSwitch" id="hotList">
<!-- 選中狀態給li 加selected -->
<ul>
<li class="selected" data-hash="cashiermodule" ></li>
<li data-hash="seckillcont"></li>
<li data-hash="powervalue"></li>
</ul>
</div>
<div id="cashiermodule" type="lists">
...
</div>
<div id="seckillcont" type="lists">
...
</div>
<div id="powervalue" type="lists">
...
</div>
複製代碼
// 監聽頁面模塊cashiermodule、seckillcont、powervalue是否進入可視區域
require('intersection-observer');
let io = new IntersectionObserver(entries => {
entries.forEach(item => {
if(item.isIntersecting && item.intersectionRatio >= 0.7) {
let target = $(item.target);
let id = target.attr('id');
oLis.removeClass('selected');
$(`[data-hash*="${id}"]`).addClass('selected');
}
})
}, {
threshold: [0.7]
})
let blocks = $('[type*="lists"]');
blocks.forEach(item => {
io.observe(item);
})
複製代碼
能夠看到,實現了模塊進入視口區域70%時,就會觸發回調,切換tab欄的按鈕選中狀態。
具體代碼以下:
// index.html
<div block="moduleA">
...
</div>
<div block="moduleB">
...
</div>
<div block="moduleC">
...
</div>
複製代碼
// 統計模塊的曝光量
require('intersection-observer');
let block = {
init: function () {
let io = new IntersectionObserver(entries => {
entries.forEach(item => {
if(item.isIntersecting) {
let target = $(item.target);
let block = target.attr('block');
window.sendPingback(block);
}
})
})
let blocks = $('[block*="module"]');
blocks.forEach(item => {
io.observe(item);
})
}
}
export default block;
複製代碼
從動畫效果中咱們看到,滑動到某一模塊,就會發送對應的統計請求。
從代碼中可知,想要統計哪一塊的曝光量,只須要在對應的div上加上block
屬性便可,方便快捷高效。後面打算在這個基礎上進行擴展,統計用戶在某一模塊的停留時間,進而獲取用戶的感興趣區域。
IntersectionObserver
存在兼容性問題,因此須要引入補丁文件npm install intersection-observer --save
複製代碼
IntersectionObserver
是異步的,不隨着目標元素的滾動同步觸發。即只有線程空閒下來,纔會執行觀察器。IntersectionObserver
還能夠實現圖片的懶加載,因爲尚未在實際項目中實踐過,這裏就不展開說明了,原理和上面兩個案例基本相同,只是須要處理圖片相關的邏輯。
掃一掃 關注個人公衆號【前端名獅】,更多精彩內容陪伴你!