10行代碼實現頁面無限滾動

背景:

因爲網頁的執行都是單線程的,在JS執行的過程當中,頁面會呈現阻塞狀態。所以,若是JS處理的數據量過大,過程複雜,可能會形成頁面的卡頓。傳統的數據展示都以分頁的形式,可是分頁的效果並很差,須要用戶手動點擊下一頁,才能看到更多的內容。有不少網站使用無限分頁的模式,即網頁視窗到達內容底部就自動加載下一部分的內容...html

原理:

實現無限分頁的過程大體以下:
   1 視窗滾動到底部;
   2 觸發加載,添加到現有內容的後面。
   所以,可能會出現兩種狀況:
   1 當頁面的內容不多,沒有出現滾動條;觸發加載頁面事件,直到加載到知足條件時中止加載;
   2 當頁面的內容不少,出現了滾動條
複製代碼

先說第一種傳統的方法

須要理解的概念

scrollHeight即真實內容的高度;api

clientHeight比較好理解,是視窗的高度,就是咱們在瀏覽器中所能看到內容的高度;數組

scrollTop是視窗上面隱藏掉的部分。瀏覽器

實現思路:

1 若是真實的內容比視窗高度小,則一直加載到超過視窗bash

2 若是超過了視窗,則判斷下面隱藏的部分的距離是否小於必定的值,若是是,則觸發加載。(即滾動到了底部)app

// 觸發條件函數
 function lowEnough(){
            var pageHeight = Math.max(document.body.scrollHeight,document.body.offsetHeight);
            var viewportHeight = window.innerHeight || 
                document.documentElement.clientHeight ||
                document.body.clientHeight || 0;
            var scrollHeight = window.pageYOffset ||
                document.documentElement.scrollTop ||
                document.body.scrollTop || 0;
            return pageHeight - viewportHeight - scrollHeight < 20;  // 經過 真實內容高度 - 視窗高度 - 上面隱藏的高度 < 20,做爲加載的觸發條件
        }
複製代碼

第二種方法:IntersectionObserver(簡單、但對瀏覽器有要求)

1、API

它的用法很是簡單。函數

var io = new IntersectionObserver(callback, option);
// 開始觀察
io.observe(document.getElementById('example'));

// 中止觀察
io.unobserve(element);

// 關閉觀察器
io.disconnect();
複製代碼

上面代碼中,IntersectionObserver是瀏覽器原生提供的構造函數,接受兩個參數:callback是可見性變化時的回調函數,option是配置對象(該參數可選)。網站

構造函數的返回值是一個觀察器實例。實例的observe方法能夠指定觀察哪一個 DOM 節點。ui

2、callback 參數

目標元素的可見性變化時,就會調用觀察器的回調函數callback。spa

callback通常會觸發兩次。一次是目標元素剛剛進入視口(開始可見),另外一次是徹底離開視口(開始不可見)。

var io = new IntersectionObserver(
  entries => {
    console.log(entries);
  }
);
複製代碼

上面代碼中,回調函數採用的是箭頭函數的寫法。callback函數的參數(entries)是一個數組,每一個成員都是一個IntersectionObserverEntry對象。舉例來講,若是同時有兩個被觀察的對象的可見性發生變化,entries數組就會有兩個成員。

3、IntersectionObserverEntry 對象

IntersectionObserverEntry對象提供目標元素的信息,一共有六個屬性。

每一個屬性的含義以下。

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

上圖中,灰色的水平方框表明視口,深紅色的區域表明四個被觀察的目標元素。它們各自的intersectionRatio圖中都已經註明。

實例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .img-area{width: 500px;height: 500px; margin: 0 auto;}
        .my-photo{width:500px; height: 300px}
    </style>
</head>
<body>
<div id="container">
    <div class="img-area"><img class="my-photo" alt="loading" src="./img/1.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" src="./img/2.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" src="./img/3.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" src="./img/4.jpg" /></div>
</div>
<div class="scrollerFooter1">
    沒有內容了
</div>
<script>
    function infinityScroll(footerNode, callback) {
        var observer = new IntersectionObserver(function(changes) {
        
            // 注意intersectionRatio這個屬性值的判斷
            if (changes[0].intersectionRatio <= 0) return;
            
            callback();
            
        });
        observer.observe(document.querySelector(footerNode));
    }
    infinityScroll('.scrollerFooter1', function() {
        for (var i = 0; i< 3; i++) {
            document.getElementById('container').appendChild(document.getElementById('container').firstChild)
        }
    });
</script>
</body>
</html>
複製代碼

固然也能夠實現懶加載:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .img-area{width: 500px;height: 500px; margin: 0 auto;}
        .my-photo{width:500px; height: 300px}
    </style>
</head>
<body>
<div class="container">
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/1.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/2.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/3.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/4.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/5.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/1.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/2.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/3.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/4.jpg" /></div>
    <div class="img-area"><img class="my-photo" alt="loading" data-src="./img/5.jpg" /></div>
</div>
<script>
    function lazyLoad(imgClassName) {
        const imgList = Array.from(document.querySelectorAll(imgClassName));
        var io = new IntersectionObserver(function (ioes) {
            ioes.forEach(function (ioe) {
                var el = ioe.target;
                var intersectionRatio = ioe.intersectionRatio;
                if (intersectionRatio > 0 && intersectionRatio <= 1) {
                    if (!el.src) {
                        el.src = el.dataset.src
                    }
                }
            })
        });
        imgList.forEach(function(item) {
            io.observe(item)
        });
    }
    lazyLoad('.my-photo');
</script>
</body>
</html>
複製代碼

最後說一下各個瀏覽器對這個API的支持,使用前需謹慎哦:

另外文中部分文字借鑑了大神——阮一峯的文章

相關文章
相關標籤/搜索