完爆scroll事件,交叉觀察器 IntersectionObserver 在千萬級PV頁面中的應用實踐

IntersectionObserver 介紹

概念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、bottomleft 四個方向的值。

var opts = { 
  root: document.querySelector('.container'),
  rootMargin: "500px 0px" 
};

var observer = new IntersectionObserver(
  callback,
  opts
);
複製代碼

應用實踐

基於這個API高性能的特性,小編在平常的業務中作了以下的實踐,使用效果很不錯,同時也簡化了業務實現邏輯。

  1. 滾動頁面,滑動到指定區域,切換TAB欄選中按鈕;
  2. 長頁面滑動時,頁面模塊的曝光量的數據統計;

1. 滾動切換TAB

效果以下圖:

具體實現代碼以下:

// 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欄的按鈕選中狀態。

2. 統計頁面模塊曝光量

具體代碼以下:

// 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屬性便可,方便快捷高效。後面打算在這個基礎上進行擴展,統計用戶在某一模塊的停留時間,進而獲取用戶的感興趣區域。

注意

  1. 因爲IntersectionObserver存在兼容性問題,因此須要引入補丁文件
npm install intersection-observer --save
複製代碼
  1. IntersectionObserver是異步的,不隨着目標元素的滾動同步觸發。即只有線程空閒下來,纔會執行觀察器。

引伸

IntersectionObserver還能夠實現圖片的懶加載,因爲尚未在實際項目中實踐過,這裏就不展開說明了,原理和上面兩個案例基本相同,只是須要處理圖片相關的邏輯。

關注我

掃一掃 關注個人公衆號【前端名獅】,更多精彩內容陪伴你!

相關文章
相關標籤/搜索