<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【JS】1067- 一個神奇的交叉觀察 API Intersection Observer

          共 27960字,需瀏覽 56分鐘

           ·

          2021-09-03 13:25

          前言

          前端開發(fā)肯定離不開判斷一個元素是否能被用戶看見,然后再基于此進行一些交互。

          過去,要檢測一個元素是否可見或者兩個元素是否相交并不容易,很多解決辦法不可靠或性能很差。然而,隨著互聯(lián)網(wǎng)的發(fā)展,這種需求卻與日俱增,比如,下面這些情況都需要用到相交檢測:

          • 圖片懶加載——當圖片滾動到可見時才進行加載
          • 內(nèi)容無限滾動——也就是用戶滾動到接近內(nèi)容底部時直接加載更多,而無需用戶操作翻頁,給用戶一種網(wǎng)頁可以無限滾動的錯覺
          • 檢測廣告的曝光情況——為了計算廣告收益,需要知道廣告元素的曝光情況
          • 在用戶看見某個區(qū)域時執(zhí)行任務(wù)或播放動畫

          過去,相交檢測通常要用到事件監(jiān)聽,并且需要頻繁調(diào)用 Element.getBoundingClientRect() 方法以獲取相關(guān)元素的邊界信息。事件監(jiān)聽和調(diào)用 Element.getBoundingClientRect()  都是在主線程上運行,因此頻繁觸發(fā)、調(diào)用可能會造成性能問題。這種檢測方法極其怪異且不優(yōu)雅。

          上面這一段話來自 MDN ,中心思想就是現(xiàn)在判斷一個元素是否能被用戶看見的使用場景越來越多,監(jiān)聽 scroll 事件以及通過 Element.getBoundingClientRect() 獲取節(jié)點位置的方式,又麻煩又不好用,那么怎么辦呢。于是就有了今天的內(nèi)容 Intersection Observer API

          Intersection Observer API 是什么

          我們需要觀察的元素被稱為 目標元素(target),設(shè)備視窗或者其他指定的元素視口的邊界框我們稱它為 根元素(root),或者簡稱為  。

          Intersection Observer API 翻譯過來叫做 “交叉觀察器”,因為判斷元素是否可見(通常情況下)的本質(zhì)就是判斷目標元素和根元素是不是產(chǎn)生了 交叉區(qū)域 。

          為什么是通常情況下,因為當我們 css 設(shè)置了 opacity: 0,visibility: hidden 或者 用其他的元素覆蓋目標元素 的時候,對于視圖來說是不可見的,但對于交叉觀察器來說是可見的。這里可能有點抽象,大家只需記住,交叉觀察器只關(guān)心 目標元素 和 根元素 是否有 交叉區(qū)域, 而不管視覺上能不能看見這個元素。當然如果設(shè)置了 display:none,那么交叉觀察器就不會生效了,其實也很好理解,因為元素已經(jīng)不存在了,那么也就監(jiān)測不到了。

          一句話總結(jié):Intersection Observer API 提供了一種異步檢測目標元素與祖先元素或 viewport 相交情況變化的方法。 -- MDN

          現(xiàn)在不懂沒關(guān)系,咱們接著往下看,看完自然就明白了。

          Intersection Observer API 怎么玩

          生成觀察器

          // 調(diào)用構(gòu)造函數(shù) IntersectionObserver 生成觀察器
          const myObserver = new IntersectionObserver(callback, options);  
          復(fù)制代碼

          首先調(diào)用瀏覽器原生構(gòu)造函數(shù) IntersectionObserver ,構(gòu)造函數(shù)的返回值是一個 觀察器實例 。

          構(gòu)造函數(shù) IntersectionObserver 接收兩個參數(shù)

          • callback: 可見性發(fā)生變化時觸發(fā)的回調(diào)函數(shù)
          • options: 配置對象(可選,不傳時會使用默認配置)

          構(gòu)造函數(shù)接收的參數(shù) options

          為了方便理解,我們先看第二個參數(shù) options 。一個可以用來配置觀察器實例的對象,那么這個配置對象都包含哪些屬性呢?

          • root: 設(shè)置目標元素的根元素,也就是我們用來判斷元素是否可見的區(qū)域,必須是目標元素的父級元素,如果不指定的話,則使用瀏覽器視窗,也就是 document。

          • rootMargin: 一個在計算交叉值時添加至根的邊界中的一組偏移量,類型為字符串 (string)  ,可以有效的縮小或擴大根的判定范圍從而滿足計算需要。語法大致和CSS 中 margin 屬性等同,默認值 “0px 0px 0px 0px” ,如果有指定 root 參數(shù),則 rootMargin 也可以使用百分比來取值。

          • threshold: 介于 0 和 1 之間的數(shù)字,指示觸發(fā)前應(yīng)可見的百分比。也可以是一個數(shù)字數(shù)組,以創(chuàng)建多個觸發(fā)點,也被稱之為 閾值。如果構(gòu)造器未傳入值, 則默認值為 0 。

          • trackVisibility: 一個布爾值,指示當前觀察器是否將跟蹤目標可見性的更改,默認為 false ,注意,此處的可見性并非指目標元素和根元素是否相交,而是指視圖上是否可見,這個我們之前就已經(jīng)分析過了,如果此值設(shè)置為 false 或不設(shè)置,那么回調(diào)函數(shù)參數(shù)中 IntersectionObserverEntry 的 isVisible 屬性將永遠返回 false 。

          • delay: 一個數(shù)字,也就是回調(diào)函數(shù)執(zhí)行的延遲時間(毫秒)。如果 trackVisibility 設(shè)置為 true,則此值必須至少設(shè)置為 100 ,否則會報錯(但是這里我也只是親測出來的,并不知道為什么會設(shè)計成這樣,如果有大佬了解請指教一下)。

          構(gòu)造函數(shù)接收的參數(shù) callback

          當元素可見比例超過指定閾值后,會調(diào)用一個回調(diào)函數(shù),此回調(diào)函數(shù)接受兩個參數(shù):存放 IntersectionObserverEntry 對象的數(shù)組和觀察器實例(可選)。

          ((doc) => {
            //回調(diào)函數(shù)
            const callback = (entries, observer) => {
              console.log('????~ 執(zhí)行了一次callback');
              console.log('????~ entries:', entries);
              console.log('????~ observer:', observer);
            };
            //配置對象
            const options = {};
            //創(chuàng)建觀察器
            const myObserver = new IntersectionObserver(callback, options);
            //獲取目標元素
            const target = doc.querySelector(".target")
            //開始監(jiān)聽目標元素
            myObserver.observe(target);
          })(document)

          我們把這兩個參數(shù)打印出來看一下,可以看到,第一個參數(shù)是一個數(shù)組,每一項都是一個目標元素對應(yīng)的 IntersectionObserverEntry對象,第二個參數(shù)是觀察器實例對象 IntersectionObserver 。

          什么是 IntersectionObserverEntry 對象

          展開 IntersectionObserverEntry 看一下都有什么。

          • boundingClientRect: 一個對象,包含目標元素的 getBoundingClientRect() 方法的返回值。

          • intersectionRatio: 一個對象,包含目標元素與根元素交叉區(qū)域 getBoundingClientRect() 的返回值。

          • intersectionRect: 目標元素的可見比例,即 intersectionRect 占 boundingClientRect 的比例,完全可見時為 1 ,完全不可見時小于等于 0 。

          • isIntersecting: 返回一個布爾值,如果目標元素與根元素相交,則返回 true ,如果 isIntersecting 是 true,則 target 元素至少已經(jīng)達到 thresholds 屬性值當中規(guī)定的其中一個閾值,如果是 false,target 元素不在給定的閾值范圍內(nèi)可見。

          • isVisible: 這個看字面意思應(yīng)該是 “是否可見” ,如果要讓這個屬性生效,那么在使用構(gòu)造函數(shù)生成觀察器實例的時候,傳入的 options 參數(shù)必須配置 trackVisibility 為 true,并且 delay 設(shè)置為大于 100 ,否則該屬性將永遠返回 false 。

          • rootBounds: 一個對象,包含根元素的 getBoundingClientRect() 方法的返回值。

          • target:: 被觀察的目標元素,是一個 DOM 節(jié)點。在觀察者包含多個目標的情況下,這是確定哪個目標元素觸發(fā)了此相交更改的簡便方法。

          • time: 該屬性提供從 首次創(chuàng)建觀察者 到 觸發(fā)此交集改變 的時間(以毫秒為單位)。通過這種方式,你可以跟蹤觀察器達到特定閾值所花費的時間。即使稍后將目標再次滾動到視圖中,此屬性也會提供新的時間。這可用于跟蹤目標元素進入和離開根元素的時間,以及兩個閾值觸發(fā)的間隔時間。

          這里再看一下 boundingClientRect ,intersectionRatio , rootBounds 三個屬性展開的內(nèi)容都有什么。

          • bottom: 元素下邊距離頁面上邊的距離
          • left: 元素左邊距離頁面左邊的距離
          • right: 元素右邊距離頁面左邊的距離
          • top: 元素上邊距離頁面上邊的距離
          • width: 元素的寬
          • height: 元素的高
          • x: 等同于 left,元素左邊距離頁面左邊的距離
          • y: 等同于 top,元素上邊距離頁面上邊的距離

          用一張圖來展示一下這幾個屬性,特別需要注意的是 right 和 bottom ,跟我們平時寫 css 的 position 那個不一樣 。

          那么第二個參數(shù) IntersectionObserver 觀察器實例對象都有什么呢

          別著急,接著往下看,實例屬性部分。

          觀察器實例屬性

          上面留了一個坑,回調(diào)函數(shù)的第二個參數(shù) IntersectionObserver 觀察器實例對象都有什么呢?我們把實例對象打印出來看一下

          ((doc) => {
            //回調(diào)函數(shù)
            const callback = () => {};
            //配置對象
            const options = {};
            //創(chuàng)建觀察器
            const myObserver = new IntersectionObserver(callback, options);
            //獲取目標元素
            const target = doc.querySelector(".target")
            //開始監(jiān)聽目標元素
            myObserver.observe(target);
            console.log('????~ myObserver:', myObserver);
          })(document)

          可以看到,我們的觀察器實例上面包含如下屬性

          • root
          • rootMargin
          • thresholds
          • trackVisibility
          • delay

          是不是特別眼熟,沒錯,就是我們創(chuàng)建觀察者實例的時候,傳入的 options 對象,只不過 options 對象是可選的,觀察器實例的屬性就使用我們傳入的 options 對象,如果沒傳就使用默認值,唯一不同的是,options 中 的屬性 threshold 是單數(shù),而我們實例獲取到的 thresholds 是復(fù)數(shù)。

          值得注意的是,這里的所有屬性都是 只讀 的,也就是說一旦觀察器被創(chuàng)建,則 無法 更改其配置,所以一個給定的觀察者對象只能用來監(jiān)聽可見區(qū)域的特定變化值。

          接下來我們就通過代碼結(jié)合動圖演示一下這些屬性

          ((doc) => {
            let n = 0
            //獲取目標元素
            const target = doc.querySelector(".target")
            //獲取根元素
            const root = doc.querySelector(".out-container")
            //回調(diào)函數(shù)
            const callback = (entries, observer) => {
              n++
              console.log(`????~ 執(zhí)行了 ${n} 次callback`);
              console.log('????~ entries:', entries);
              console.log('????~ observer:', observer);
            };
            //配置對象
            const options = {
              root: root,
              rootMargin'0px 0px 0px 0px',
              threshold: [0.5],
              trackVisibilitytrue,
              delay100
            };
            //創(chuàng)建觀察器
            const myObserver = new IntersectionObserver(callback, options);
            //開始監(jiān)聽目標元素
            myObserver.observe(target);
            console.log('????~ myObserver:', myObserver);
          })(document)

          root這個沒什么說的,就是設(shè)置指定節(jié)點為根元素rootMargin我們把 rootMargin 修改為 '50px 50px 50px 50px',可以看到,我們的目標元素還沒有露出來的時候回調(diào)函數(shù)就已經(jīng)執(zhí)行了,也就是說目標元素距離根元素還有 50px 的 margin 時,觀察器就認為是發(fā)生了交叉。thresholds我們把 threshold 修改為 [0.1, 0.3, 0.5, 0.8, 1],可以看到,回調(diào)函數(shù)觸發(fā)了多次,也就是說當交叉區(qū)域的百分比,每達到指定的閾值時都會觸發(fā)一次回調(diào)函數(shù)。

          注意 Intersection Observer API 無法提供重疊的像素個數(shù)或者具體哪個像素重疊,他的更常見的使用方式是——當兩個元素相交比例在 N% 左右時,觸發(fā)回調(diào),以執(zhí)行某些邏輯。 -- MDN

          trackVisibility修改 trackVisibility 為 true ,可以看到, isVisible 屬性值為 true 。修改 css 屬性 為 opacity: 0,可以看到,雖然我們藍色小方塊并沒有出現(xiàn)在視圖中,但是回調(diào)函數(shù)已經(jīng)執(zhí)行了,并且 isVisible 屬性值為 false 而 isIntersecting 值為 true 。delay回調(diào)函數(shù)延遲觸發(fā),我們修改 delay 為 3000,可以看到 log 是 3000ms 以后才輸出的。

          觀察器實例方法

          通過此段代碼來演示觀察器實例方法,為了方便演示,我添加了幾個對應(yīng)的按鈕。

          ((doc) => {
            let n = 0
            //獲取目標元素
            const target1 = doc.querySelector(".target1")
            const target2 = doc.querySelector(".target2")
            //添加幾個按鈕方便操作
            const observe = doc.querySelector(".observe")
            const unobserve = doc.querySelector(".unobserve")
            const disconnect = doc.querySelector(".disconnect")
            observe.addEventListener('click', () => myObserver.observe(target1))
            unobserve.addEventListener('click', () => myObserver.unobserve(target1))
            disconnect.addEventListener('click', () => myObserver.disconnect())
            //獲取根元素
            const root = doc.querySelector(".out-container")
            //回調(diào)函數(shù)
            const callback = (entries, observer) => {
              n++
              console.log(`????~ 執(zhí)行了 ${n} 次callback`);
              console.log('????~ entries:', entries);
              console.log('????~ observer:', observer);
            };
            //配置對象
            const options = {
              root: root,
              rootMargin'0px 0px 0px 0px',
              threshold: [0.10.20.30.5],
              trackVisibilitytrue,
              delay100
            };
            //創(chuàng)建觀察器
            const myObserver = new IntersectionObserver(callback, options);
            //開始監(jiān)聽目標元素
            myObserver.observe(target2);
            console.log('????~ myObserver:', myObserver);
          })(document)

          observe

           const myObserver = new IntersectionObserver(callback, options);
           myObserver.observe(target);

          接受一個目標元素作為參數(shù)。很好理解,當我們創(chuàng)建完觀察器實例后,要手動的調(diào)用 observe 方法來通知它開始監(jiān)測目標元素。

          可以在同一個觀察者對象中配置監(jiān)聽多個目標元素

          target2 元素是通過代碼自動監(jiān)測的,而 target1 則是我們在點擊了 observe 按鈕之后開始監(jiān)測的。通過動圖可以看到,當我單擊 observe 按鈕后,我們的 entries 數(shù)組里面就包含了兩條數(shù)據(jù),前文中說到,可以通過 target 屬性來判斷是哪個目標元素。

          unobserve

           const myObserver = new IntersectionObserver(callback, options);
           myObserver.observe(target);
           myObserver.unobserve(target)
          復(fù)制代碼

          接收一個目標元素作為參數(shù),當我們不想監(jiān)聽某個元素的時候,需要手動調(diào)用 unobserve 方法來停止監(jiān)聽指定目標元素。通過動圖可以發(fā)現(xiàn),當我們點擊 unobserve 按鈕后,由兩條數(shù)據(jù)變成了一條數(shù)據(jù),說明 target1 已經(jīng)不再接受監(jiān)測了。

          disconnect

           const myObserver = new IntersectionObserver(callback, options);
           myObserver.disconnect()

          當我們不想監(jiān)測任何一個目標元素時,我們需要手動調(diào)用 disconnect 方法停止監(jiān)聽工作。通過動圖可以看到,當我們點擊 disconnect 按鈕后,控制臺不再輸出 log ,說明監(jiān)聽工作已經(jīng)停止,可以通過 observe 再次開啟監(jiān)聽工作。

          takeRecords

          返回所有觀察目標的 IntersectionObserverEntry 對象數(shù)組,應(yīng)用場景較少。

          當觀察到交互動作發(fā)生時,回調(diào)函數(shù)并不會立即執(zhí)行,而是在空閑時期使用 requestIdleCallback 來異步執(zhí)行回調(diào)函數(shù),但是也提供了同步調(diào)用的 takeRecords 方法。

          如果異步的回調(diào)先執(zhí)行了,那么當我們調(diào)用同步的 takeRecords 方法時會返回空數(shù)組。同理,如果已經(jīng)通過 takeRecords 獲取了所有的觀察者實例,那么回調(diào)函數(shù)就不會被執(zhí)行了。

          注意事項

          構(gòu)造函數(shù) IntersectionObserver 配置的回調(diào)函數(shù)都在哪些情況下被調(diào)用?

          構(gòu)造函數(shù) IntersectionObserver 配置的回調(diào)函數(shù),在以下情況發(fā)生時可能會被調(diào)用

          • 當目標(target)元素與根(root)元素發(fā)生交集的時候執(zhí)行。
          • 兩個元素的相交部分大小發(fā)生變化時。
          • Observer 第一次監(jiān)聽目標元素的時候。
          ((doc) => {
            //回調(diào)函數(shù)
            const callback = () => {
              console.log('????~ 執(zhí)行了一次callback');
            };
            //配置對象
            const options = {};
            //觀察器實例
            const myObserver = new IntersectionObserver(callback, options);
            //目標元素
            const target = doc.querySelector("#target")
            //開始觀察
            myObserver.observe(target);
          })(document)

          可以看到,無論目標元素是否與根元素相交,當我們第一次監(jiān)聽目標元素的時候,回調(diào)函數(shù)都會觸發(fā)一次,所以不要直接在回調(diào)函數(shù)里寫邏輯代碼,盡量通過 isIntersecting 或者 intersectionRect 進行判斷之后再執(zhí)行邏輯代碼。

          頁面的可見性如何監(jiān)測

          頁面的可見性可以通過document.visibilityState或者document.hidden獲得。頁面可見性的變化可以通過document.visibilitychange來監(jiān)聽。

          可見性和交叉觀察

          當 css 設(shè)置了opacity: 0,visibility: hidden 以及 用其他的元素覆蓋目標元素 ,都不會影響交叉觀察器的監(jiān)測,也就是都不會影響 isIntersecting 屬性的結(jié)果,但是會影響 isVisible 屬性的結(jié)果, 如果元素設(shè)置了 display:none 就不會被檢測了。當然影響元素可視性的屬性不止上述這些,還包括position,margin,clip 等等等等...就靠小伙伴們自行發(fā)掘了

          交集的計算

          所有區(qū)域均被 Intersection Observer API 當做一個 矩形 看待。如果元素是不規(guī)則的圖形也將會被看成一個包含元素所有區(qū)域的最小矩形,相似的,如果元素發(fā)生的交集部分不是一個矩形,那么也會被看作是一個包含他所有交集區(qū)域的最小矩形。

          我怎么知道目標元素來自視口的上方還是下方

          目標元素滾動的方向也是可以判斷的,原理是根元素的 entry.rootBounds.y 是固定不變的 ,所以我們只需要計算 entry.boundingClientRect.y 與 entry.rootBounds.y 的大小,當回調(diào)函數(shù)觸發(fā)的時候,我們記錄下當時的位置,如果 entry.boundingClientRect.y < entry.rootBounds.y,說明是在視口下方,那么當下一次目標元素可見的時候,我們就知道目標元素時來自視口下方的,反之亦然。

          let wasAbove = false;
          function callback(entries, observer{
              entries.forEach(entry => {
                  const isAbove = entry.boundingClientRect.y < entry.rootBounds.y;
                  if (entry.isIntersecting) {
                      if (wasAbove) {
                          // Comes from top
                      }
                  }
                  wasAbove = isAbove;
              });
          }

          應(yīng)用場景

          介紹完基礎(chǔ)知識,總得來幾個實例(演示代碼采用VUE3.0),當然實際場景要比這復(fù)雜的多,如何在自己的工作學(xué)習中應(yīng)用,還是要靠小伙伴們多多開動聰明的大腦~

          數(shù)據(jù)列表無限滾動

          <template>
            <div class="box">
              <div class="vbody"
                   v-for='item in list'
                   :key='item'>
          內(nèi)容區(qū)域{{item}}</div>
              <div class="reference"
                   ref='reference'>
          </div>
            </div>

          </template>

          <script lang='ts'>
          import { defineComponent, onMounted, reactive, ref } from 'vue'

          export default defineComponent({
            name: '',
            setup() {
              const reference = ref(null)
              const list = reactive([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
              onMounted(() => {
                let n = 10
                /
          /回調(diào)函數(shù)
                const callback = (entries) => {
                  const myEntry = entries[0]
                  if (myEntry.isIntersecting) {
                    console.log(`????~ 觸發(fā)了無線滾動,開始模擬請求數(shù)據(jù) ${n}`)
                    n++
                    list.push(n)
                  }
                }
                /
          /配置對象
                const options = {
                  root: null,
                  rootMargin: '0px 0px 0px 0px',
                  threshold: [0, 1],
                  trackVisibility: true,
                  delay: 100,
                }
                /
          /觀察器實例
                const myObserver = new IntersectionObserver(callback, options)
                /
          /開始觀察
                myObserver.observe(reference.value)
              })

              return { reference, list }
            },
          })
          </
          script>

          <style>
          * {
            margin0;
            padding0;
            box-sizing: border-box;
          }
          .reference {
            width100%;
            visibility: hidden;
          }
          .vbody {
            width100%;
            height200px;
            background-color: red;
            color: aliceblue;
            font-size40px;
            text-align: center;
            line-height200px;
            margin10px 0;
          }
          </style>

          我們只需要在底部添加一個參考元素,當參考元素可見時,就向后臺請求數(shù)據(jù),就可以實現(xiàn)無線滾動的效果了。

          圖片預(yù)加載

          <template>
            <div class="box">
              <div class="vbody">內(nèi)容區(qū)域</div>
              <div class="vbody">內(nèi)容區(qū)域</div>
              <div class="header"
                   ref='header'>

                <img :src="url">
              </div>
              <div class="vbody">內(nèi)容區(qū)域</div>
            </div>
          </template>


          <script lang='ts'>
          import { defineComponent, onMounted, ref } from 'vue'

          export default defineComponent({
            name'',
            setup() {
              const header = ref(null)
              const url = ref('')
              onMounted(() => {
                //回調(diào)函數(shù)
                const callback = (entries) => {
                  const myEntry = entries[0]
                  if (myEntry.isIntersecting) {
                    console.log('????~ 預(yù)加載圖片開始')
                    url.value =
                      '//img10.360buyimg.com/imgzone/jfs/t1/197235/15/2956/67824/6115e076Ede17a418/d1350d4d5e52ef50.jpg'
                  }
                }
                //配置對象
                const options = {
                  rootnull,
                  rootMargin'200px 200px 200px 200px',
                  threshold: [0],
                  trackVisibilitytrue,
                  delay100,
                }
                //觀察器實例
                const myObserver = new IntersectionObserver(callback, options)
                //開始觀察
                myObserver.observe(header.value)
              })

              return { header, url }
            },
          })
          </script>


          <style>
          * {
            margin0;
            padding0;
            box-sizing: border-box;
          }
          .box {
          }
          .header {
            width100%;
            height400px;
            background-color: blue;
            color: aliceblue;
            font-size40px;
            text-align: center;
            line-height400px;
          }
          .header img {
            width100%;
            height100%;
          }
          .reference {
            width100%;
            visibility: hidden;
          }
          .vbody {
            width100%;
            height800px;
            background-color: red;
            color: aliceblue;
            font-size40px;
            text-align: center;
            line-height800px;
            margin10px 0;
          }
          </style>

          利用 options 的 rootMargin屬性,可以在圖片即將進入可視區(qū)域的時間進行圖片的加載,即避免了提前請求大量圖片造成的性能問題,也避免了圖片進入窗口才加載已經(jīng)來不及的問題。

          吸頂

          <template>
            <div class="box">
              <div class="reference"
                   ref='reference'>
          </div>
              <div class="header"
                   ref='header'>
          吸頂區(qū)域</div>
              <div class="vbody">內(nèi)容區(qū)域</div>
              <div class="vbody">內(nèi)容區(qū)域</div>
              <div class="vbody">內(nèi)容區(qū)域</div>
            </div>

          </template>

          <script lang='ts'>
          import { defineComponent, onMounted, ref } from 'vue'

          export default defineComponent({
            name: '',
            setup() {
              const header = ref(null)
              const reference = ref(null)

              onMounted(() => {
                /
          /回調(diào)函數(shù)
                const callback = (entries) => {
                  const myEntry = entries[0]
                  if (!myEntry.isIntersecting) {
                    console.log('????~ 觸發(fā)了吸頂')
                    header.value.style.position = 'fixed'
                    header.value.style.top = '0px'
                  } else {
                    console.log('????~ 取消吸頂')
                    header.value.style.position = 'relative'
                  }
                }
                /
          /配置對象
                const options = {
                  root: null,
                  rootMargin: '0px 0px 0px 0px',
                  threshold: [0, 1],
                  trackVisibility: true,
                  delay: 100,
                }
                /
          /觀察器實例
                const myObserver = new IntersectionObserver(callback, options)
                /
          /開始觀察
                myObserver.observe(reference.value)
              })

              return { reference, header }
            },
          })
          </
          script>

          <style>
          * {
            margin0;
            padding0;
            box-sizing: border-box;
          }
          .header {
            width100%;
            height100px;
            background-color: blue;
            color: aliceblue;
            font-size40px;
            text-align: center;
            line-height100px;
          }
          .reference {
            width100%;
            visibility: hidden;
          }
          .vbody {
            width100%;
            height800px;
            background-color: red;
            color: aliceblue;
            font-size40px;
            text-align: center;
            line-height800px;
            margin10px 0;
          }
          </style>

          思路就是利用一個參考元素作為交叉觀察器觀察的對象,當參考元素可見的時候,取消吸頂區(qū)域的 fixed 屬性,否則添加 fixed 屬性,吸底稍微復(fù)雜一點,但是道理差不多,留給小伙伴們自行研究吧 ~ ~。

          埋點上報

          <template>
            <div class="box">
              <div class="vbody">內(nèi)容區(qū)域</div>
              <div class="vbody">內(nèi)容區(qū)域</div>
              <div class="header"
                   ref='header'>
          埋點區(qū)域</div>
              <div class="vbody">內(nèi)容區(qū)域</div>

            </div>

          </template>

          <script lang='ts'>
          import { defineComponent, onMounted, ref } from 'vue'

          export default defineComponent({
            name: '',
            setup() {
              const header = ref(null)

              onMounted(() => {
                /
          /回調(diào)函數(shù)
                const callback = (entries) => {
                  const myEntry = entries[0]
                  if (myEntry.isIntersecting) {
                    console.log('????~ 觸發(fā)了埋點')
                  }
                }
                /
          /配置對象
                const options = {
                  root: null,
                  rootMargin: '0px 0px 0px 0px',
                  threshold: [0.5],
                  trackVisibility: true,
                  delay: 100,
                }
                /
          /觀察器實例
                const myObserver = new IntersectionObserver(callback, options)
                /
          /開始觀察
                myObserver.observe(header.value)
              })

              return { header }
            },
          })
          </
          script>

          <style>
          * {
            margin0;
            padding0;
            box-sizing: border-box;
          }
          .header {
            width100%;
            height400px;
            background-color: blue;
            color: aliceblue;
            font-size40px;
            text-align: center;
            line-height400px;
          }
          .vbody {
            width100%;
            height800px;
            background-color: red;
            color: aliceblue;
            font-size40px;
            text-align: center;
            line-height800px;
            margin10px 0;
          }
          </style>

          通常情況下,我們統(tǒng)計一個元素是否被用戶有效的看到,并不是元素剛出現(xiàn)就觸發(fā)埋點,而是元素進入可是區(qū)域一定比例才可以,我們可以配置 options 的 threshold 為 0.5。

          等等等等。。。。

          這個 api 可以說是非常強大了,可玩性也是極高,大家自由發(fā)揮 ~ ~

          兼容性

          為什么有兩張兼容性的圖呢?因為 trackVisibility 和 delay 兩個屬性是屬于 IntersectionObserver V2 的。所以小伙伴們在用的時候一定要注意兼容性。當然也有兼容解決方案,那就是 intersection-observer-polyfill

          參考資料

          [1] Can I Use:

          https://caniuse.com/?search=IntersectionObserver%20

          [2] MDN Intersection Observer:

          https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver

          [3] IntersectionObserver API 使用教程:

          https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html

          [4] intersection-observer-polyfill:

          https://www.npmjs.com/package/intersection-observer-polyfill


          瀏覽 90
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  四房婷婷五月天 | 黄色一级视频在线观看 | 亚洲乱72 | 国产A√ | aa色黄视频 |