無限滾動對咱們來講已是很常見的功能了,具體表現爲當頁面滾動到某個位置時就自動加載數據,本文將探討無限滾動的實現原理以及優化。git
咱們先看看最簡單的無限滾動的例子:github
function fetchData() { fetch(path).then(res => doSomeThing(res.data)); } window.addEventListener('scroll', fetchData);
上面就是無限滾動最簡單的例子啦~
其實就是監聽 window
對象的 scroll
事件,而後再觸發獲取數據的函數~api
然而,上面的例子中還有不少問題,其中最大的問題就是 獲取數據的函數(之後叫 fetch 函數)沒有觸發條件, 咱們還須要不斷優化,才能在生產環境下使用。瀏覽器
咱們先想一想,通常狀況下,fetch 函數的觸發條件有哪些呢 ?函數
在 fetch 過程當中不能重複觸發fetch
沒有更多數據的時候不能再觸發優化
屏幕距離容器邊緣 xxx 的時候觸發code
前兩點很好處理,只要加個 isLoading
和 isEnd
的變量就能夠了。
添加這兩個變量以後,咱們的代碼就變成下面的樣子啦:對象
var isLoading = false; var isEnd = false; function fetchData() { if ( !isLoading && !isEnd ) { isLoading = true; fetch(path).then(res => { isLoading = false; res.data.length === 0 && isEnd = true; doSomething(res.data); }); } } window.addEventListener('scroll', fetchData);
第三點對不熟悉 DOM 的童鞋來講就有點難度了~事件
咱們以計算屏幕底部與容器底部邊緣爲例:
若是有 api 能夠直接獲得元素底部與屏幕底部的距離就最好啦,能夠省去麻煩,但實際上並無這樣的 api。
然而,咱們能夠經過瀏覽器提供的兩個 api,計算出元素底部與屏幕底部之間的距離。
第一個 api 是 window.innerHeight
,它返回的是屏幕(viewport)高度。
第二個 api 就是 Element.getBoundingClientRect
,這個方法用來計算元素邊緣與屏幕(viewport)之間的距離。
須要提醒一下,Element.getBoundingClientRect
會獲得這麼一個類 Object 對象:
ClientRect { width: 760, // 元素寬度 height: 2500, // 元素高度 top: -1352, // 元素上邊緣與屏幕上邊緣的距離 bottom: 1239, // 元素下邊緣與屏幕上邊緣的距離 left: 760, // 元素左邊緣與屏幕左邊緣的距離 right: 860 // 元素右邊緣與屏幕左邊緣的距離 }
能夠看看下面這圖:
+------> +--------------------------------------------------------+ | | document.body | | | | | | | body.getBoundingClientRect().top | | | | | | | | +--------------------------------------------------------+ | | browser x | +------> +--------------------------------------------------------+ <--+ | | window | | | | | | | | | | | | | | | | | | | | | | | | | | window.innerHeight | | | | | | | | | | | | body.getBoundingClientRect().bottom | | | | | | | | | | | | | | | | | | | | +------> +--------------------------------------------------------+ | | | | | | | | | | | | | | | | | | | +--------------------------------------------------------+ <--+
有了這兩個 api,咱們很容易就能夠計算出元素底部邊緣與屏幕底部邊緣的位置啦~
咱們再修改下咱們的代碼:
var isLoading = false; var isEnd = false; var triggerDistance = 200; function fetchData() { var distance = container.getBoundingClientRect().bottom - window.innerHeight; if ( !isLoading && !isEnd && distance < triggerDistance ) { isLoading = true; fetch(path).then(res => { isLoading = false; res.data.length === 0 && isEnd = true; doSomething(res.data); }); } } window.addEventListener('scroll', fetchData);
修改以後,當容器底部與屏幕底部距離小於 200 的時候,纔會觸發 fetch 函數,這樣咱們的無限滾動就更加實用啦!
然而,並非只有 window 才能夠滾動,擁有高度的級塊元素只要設置了 overflow: scroll
都是能夠滾動的。
咱們須要再修改一下代碼來讓級塊元素也支持無限滾動!
function fetchData() { /* do something */ } window.addEventListener('scroll', fetchData); document.getElementById('container').addEventListener('scroll', fetchData);
很簡單吧!只須要爲該容器元素添加一個 scroll 的事件監聽器就好啦!
http://scarletsky.github.io/2016/04/20/how-to-implement-infinite-scroll/
https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect