IntersectionObserver對象

IntersectionObserver對象

IntersectionObserver對象,從屬於Intersection Observer API,提供了一種異步觀察目標元素與其祖先元素或頂級文檔視窗viewport交叉狀態的方法,祖先元素與視窗viewport被稱爲根root,也就是說IntersectionObserver API,可以自動觀察元素是否可見,由於可見visible的本質是,目標元素與視口產生一個交叉區,所以這個API叫做交叉觀察器,兼容性https://caniuse.com/?search=IntersectionObserver

描述

IntersectionObserver解決了一個長期以來Web的問題,觀察元素是否可見,這個可見visible的本質是,目標元素與視口產生一個交叉區,所以這個API叫做交叉觀察器。
要檢測一個元素是否可見或者兩個元素是否相交併不容易,很多解決辦法不可靠或性能很差。現在很多需求下都需要用到相交檢測,例如圖片懶加載、內容無限滾動、檢測元素的曝光情況、可視區域播放動畫等等,相交檢測通常要用到onscroll事件監聽,並且可能需要頻繁調用Element.getBoundingClientRect()等方法以獲取相關元素的邊界信息,事件監聽和調用Element.getBoundingClientRect都是在主線程上運行,因此頻繁觸發、調用可能會造成性能問題,這種檢測方法極其怪異且不優雅。
Intersection Observer API會註冊一個回調函數,每當被監視的元素進入或者退出另外一個元素時或viewport,或者兩個元素的相交部分大小發生變化時,該回調方法會被觸發執行,這樣網站的主線程不需要再爲了監聽元素相交而辛苦勞作,瀏覽器會自行優化元素相交管理,注意Intersection Observer API無法提供重疊的像素個數或者具體哪個像素重疊,他的更常見的使用方式是當兩個元素相交比例在N%左右時,觸發回調,以執行某些邏輯。

const io = new IntersectionObserver(callback, option);

// 開始觀察
io.observe(document.getElementById("example"));
// 停止觀察
io.unobserve(element);
// 關閉觀察器
io.disconnect();
  • 參數callback,創建一個新的IntersectionObserver對象後,當其監聽到目標元素的可見部分穿過了一個或多個閾thresholds時,會執行指定的回調函數。
  • 參數optionIntersectionObserver構造函數的第二個參數是一個配置對象,其可以設置以下屬性:
    • threshold屬性決定了什麼時候觸發回調函數,它是一個數組,每個成員都是一個門檻值,默認爲[0],即交叉比例intersectionRatio達到0時觸發回調函數,用戶可以自定義這個數組,比如[0, 0.25, 0.5, 0.75, 1]就表示當目標元素0%25%50%75%100%可見時,會觸發回調函數。
    • root屬性指定了目標元素所在的容器節點即根元素,目標元素不僅會隨着窗口滾動,還會在容器裏面滾動,比如在iframe窗口裏滾動,這樣就需要設置root屬性,注意,容器元素必須是目標元素的祖先節點。
    • rootMargin屬性定義根元素的margin,用來擴展或縮小rootBounds這個矩形的大小,從而影響intersectionRect交叉區域的大小,它使用CSS的定義方法,比如10px 20px 30px 40px,表示toprightbottomleft四個方向的值。
  • 屬性IntersectionObserver.root只讀,所監聽對象的具體祖先元素element,如果未傳入值或值爲null,則默認使用頂級文檔的視窗。
  • 屬性IntersectionObserver.rootMargin只讀,計算交叉時添加到根root邊界盒bounding box的矩形偏移量,可以有效的縮小或擴大根的判定範圍從而滿足計算需要,此屬性返回的值可能與調用構造函數時指定的值不同,因此可能需要更改該值,以匹配內部要求,所有的偏移量均可用像素pixelpx或百分比percentage%來表達,默認值爲0px 0px 0px 0px
  • 屬性IntersectionObserver.thresholds只讀,一個包含閾值的列表,按升序排列,列表中的每個閾值都是監聽對象的交叉區域與邊界區域的比率,當監聽對象的任何閾值被越過時,都會生成一個通知Notification,如果構造器未傳入值,則默認值爲0
  • 方法IntersectionObserver.disconnect(),使IntersectionObserver對象停止監聽工作。
  • 方法IntersectionObserver.observe(),使IntersectionObserver開始監聽一個目標元素。
  • 方法IntersectionObserver.takeRecords(),返回所有觀察目標的IntersectionObserverEntry對象數組。
  • 方法IntersectionObserver.unobserve(),使IntersectionObserver停止監聽特定目標元素。

此外當執行callback函數時,會傳遞一個IntersectionObserverEntry對象參數,其提供的信息如下。

  • time:可見性發生變化的時間,是一個高精度時間戳,單位爲毫秒。
  • target:被觀察的目標元素,是一個DOM節點對象。
  • rootBounds:根元素的矩形區域的信息,是getBoundingClientRect方法的返回值,如果沒有根元素即直接相對於視口滾動,則返回null
  • boundingClientRect:目標元素的矩形區域的信息。
  • intersectionRect:目標元素與視口或根元素的交叉區域的信息。
  • intersectionRatio:目標元素的可見比例,即intersectionRectboundingClientRect的比例,完全可見時爲1,完全不可見時小於等於0

應用

實現一個使用IntersectionObserver的簡單示例,兩個方塊分別可以演示方塊1是否在屏幕可見區域內以及方塊2是否在方塊1的相對可見交叉區域內,另外可以使用IntersectionObserver可以進行首屏渲染的優化,可以參考https://github.com/WindrunnerMax/EveryDay/blob/master/Vue/Vue%E9%A6%96%E5%B1%8F%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%BB%84%E4%BB%B6.md

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style> 
        body{
            margin: 0;
            padding: 0;
            height: 100vh;
            width: 100vw;
            overflow-x: hidden;
        }
        .flex{
            display: flex;
        }
        .top-fixed{
            top: 0;
            position: fixed;
        }
        .placeholder1{
            width: 100%;
        }
        #box1{
            height: 200px; 
            overflow-y: auto; 
            border: 1px solid #aaa; 
            width: 60%;
        }
        .box1-placeholder{
            height: 105vh;
        }
        #box2{
            height: 100px; 
            background-color: blue; 
            margin-top: 300px; 
            width: 60%;
        }
        .box2-placeholder{
            height: 205px;
        }
    </style>
</head>
<body>
    <section class="flex top-fixed">
        <div class="flex">BOX1:</div>
        <div class="flex" id="box1-status">invisible</div>
        <div class="flex">&nbsp;BOX2:</div>
        <div class="flex" id="box2-status">invisible</div>
    </section>
    <div class="box1-placeholder"></div>
    <div id="box1">
        <div class="box2-placeholder"></div>
        <div id="box2"></div>   
        <div class="box2-placeholder"></div>
    </div>
    <div class="box1-placeholder"></div>

</body>
<script>
    (function(){
        const box1 = document.querySelector("#box1");
        const box2 = document.querySelector("#box2");
        const box1Status = document.querySelector("#box1-status");
        const box2Status = document.querySelector("#box2-status");
        const box1Observer = new IntersectionObserver(entries => {
            entries.forEach(item => {
                // `intersectionRatio`爲目標元素的可見比例,大於`0`代表可見
                if (item.intersectionRatio > 0) {
                    box1Status.innerText = "visible";
                }else{
                    box1Status.innerText = "invisible";
                }
            });
        }, {root: document});
        const box2Observer = new IntersectionObserver(entries => {
            entries.forEach(item => {
                // `intersectionRatio`爲目標元素的可見比例,大於`0`代表可見
                if (item.intersectionRatio > 0) {
                    box2Status.innerText = "visible";
                }else{
                    box2Status.innerText = "invisible";
                }
            });
        }, {root: box1});
        box1Observer.observe(box1);
        box2Observer.observe(box2);
    })();
</script>
</html>

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://www.jianshu.com/p/eadd83d794c8
https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章