背景:
由於網頁的執行都是單執行緒的,在JS執行的過程中,頁面會呈現阻塞狀態。因此,如果JS處理的資料量過大,過程複雜,可能會造成頁面的卡頓。傳統的資料展現都以分頁的形式,但是分頁的效果並不好,需要使用者手動點選下一頁,才能看到更多的內容。有很多網站使用無限分頁的模式,即網頁視窗到達內容底部就自動載入下一部分的內容...
原理:
實現無限分頁的過程大致如下:
1 視窗滾動到底部;
2 觸發載入,新增到現有內容的後面。
因此,可能會出現兩種情況:
1 當頁面的內容很少,沒有出現滾動條;觸發載入頁面事件,直到載入到滿足條件時停止載入;
2 當頁面的內容很多,出現了滾動條
複製程式碼
先說第一種傳統的方法
需要理解的概念
scrollHeight即真實內容的高度;
clientHeight比較好理解,是視窗的高度,就是我們在瀏覽器中所能看到內容的高度;
scrollTop是視窗上面隱藏掉的部分。
實現思路:
1 如果真實的內容比視窗高度小,則一直載入到超過視窗
2 如果超過了視窗,則判斷下面隱藏的部分的距離是否小於一定的值,如果是,則觸發載入。(即滾動到了底部)
// 觸發條件函式
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(簡單、但對瀏覽器有要求)
一、API
它的用法非常簡單。
var io = new IntersectionObserver(callback, option);
// 開始觀察
io.observe(document.getElementById('example'));
// 停止觀察
io.unobserve(element);
// 關閉觀察器
io.disconnect();
複製程式碼
上面程式碼中,IntersectionObserver是瀏覽器原生提供的建構函式,接受兩個引數:callback是可見性變化時的回撥函式,option是配置物件(該引數可選)。
建構函式的返回值是一個觀察器例項。例項的observe方法可以指定觀察哪個 DOM 節點。
二、callback 引數
目標元素的可見性變化時,就會呼叫觀察器的回撥函式callback。
callback一般會觸發兩次。一次是目標元素剛剛進入視口(開始可見),另一次是完全離開視口(開始不可見)。
var io = new IntersectionObserver(
entries => {
console.log(entries);
}
);
複製程式碼
上面程式碼中,回撥函式採用的是箭頭函式的寫法。callback函式的引數(entries)是一個陣列,每個成員都是一個IntersectionObserverEntry物件。舉例來說,如果同時有兩個被觀察的物件的可見性發生變化,entries陣列就會有兩個成員。
三、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的支援,使用前需謹慎哦:
另外文中部分文字借鑑了大神——阮一峰的文章