現在在手機端列表滾動隨處可見,拿現在大家都在用的微信來說,朋友圈就是一個列表,和好友發信息界面就是一個列表。
如果列表長度不長,比如你微信朋友圈一共就只有兩三個人分享過他們的動態,那沒問題,如果你是大屏手機估計還不用滾動就已經看完了。
但那有可能嗎?有也只是少數。我們現在每天刷朋友圈那都是幾屏幾屏這樣刷的。
我們設定一屏有五個人分享的動態,每一個動態里面有5個dom節點,即一屏有25個dom節點。
我們開始刷朋友圈,才看了四屏就已經100個節點了,再往下刷成百甚至上千個dom節點都出來了,那你手機豈不是卡成翔了。
但我們現實中刷朋友圈會越往下刷越卡嗎?不會。為什么呢?
這就是今天要說的一個優化手機端列表滾動性能的方法——分頁加載
什么是分頁加載呢,大致意思就是把一個列表分成很多很多頁,我一開始只給你顯示若干頁(N),當我往下滾動到底部的時候,顯示下一頁,同時把最上面那一頁移除。
同樣道理,往上滾動時則顯示上一頁,同時把最下面那一頁移除。這樣,顯示的就永遠只有N頁,dom節點也就永遠只有Nx25個。
那么分頁加載需要處理哪些問題呢?
1. 往下滾動的時候把最上面一頁移除,那等我要往上滾動的時候,上一頁都已經被移除掉了,我怎么獲取到上一頁的內容呢?
2. 既然有dom節點的移除和添加,那么我的滾動條位置要移除頁面的同時要怎么設置才能讓用戶看起來像是無縫連接呢?
3. 我如何判斷我已經滾動到了頁面的頂部或底部?
4. 在滾動到底部或頂部的時候,我們具體究竟要執行哪些操作。
我們一個個來解決。
第一個問題我們只需要用兩個數組來存取被移除掉的內容就OK了。
var preRemoveArray = []; //被移除的當前頁面之前的頁面 var nextRemoveArray = []; //被移除的當前頁面后面的頁面
當我們要移除上一頁的時候,只需要獲得第一頁的html,將它push進preRemoveArray就可以了。同時如果nextRemoveArray中有內容的話(意味着你之前已經瀏覽過下面的內容),只需要執行
nextRemoveArray.pop(nextRemoveArray.length - 1);
就可以獲取到下一頁的內容了
第二個問題,關於滾動條的位置,我們分兩種情況。
1.往下拉
我們通過document.body.scrollTop獲得當前滾動條的位置,因為往下拉要移除第一頁的內容,所以
document.body.scrollTop = document.body.scrollTop - 第一頁的高度
2.往上拉
假設我們現在頁面中顯示的是第2、3頁,此時滾動到頂部,document.body.scrollTop = 0,加載了上一頁的內容,移除了第3頁之后,頁面中顯示的是第1、2頁,
我們的滾動條要設置在第2頁的頂部,這樣用戶看起來才像是無縫連接的,所以
document.body.scrollTop = document.body.scrollTop + 第一頁的高度
3.判斷是否到頂部很簡單
var isTop = function() { return document.body.scrollTop === 0; };
返回true就是到頂部了
判斷是否到底部需要獲取幾個值,
一個是document.body.scrollTop,當前滾動條的位置
一個是window.screen.height,當前屏幕的高度
一個是document.body.clientHeight, 當前整個頁面的總高度
只有當document.body.scrollTop + window.screen.height = document.body.clientHeight時,即
var isBottom = function() { return document.body.scrollTop + window.screen.height == document.body.clientHeight; };
返回true時,則滾動到了底部。
4. 在滾動到頂部的時候,我們需要判斷preRemoveArray里是否有值,有則意味着前面有頁面,沒有則意味着這是第一頁,第一頁的話我們不需要做任何處理。
如果不是第一頁的話,我們要執行
1). 獲取當前列表中的最后一頁,並存進nextRemoveArray數組里
2). 將上一頁的內容添加進列表中
3). 設置滾動條的位置
4). 將最后一頁移除
往下滾動也是同一個道理,只是無論nextRemoveArray里面是否有值我們都要進行相應的操作。
有值則去nextRemoveArray里上一頁的內容添加進列表
沒有值則生成新的節點,添加到列表中。
最終代碼如下:
var PAGE = 1; //初始化頁數 var PAGESIZE = 5; //每頁展示幾條數據 var $list = $('#list'); //列表 var preRemoveArray = []; //被移除的當前頁面之前的頁面 var nextRemoveArray = []; //被移除的當前頁面后面的頁面 var init = function() { initPage(); initEvent(); }; var initPage = function() { renderData(); }; var initEvent = function() { var $page; var length; window.onscroll = function() { if (isTop()) { if (preRemoveArray[0]) { length = preRemoveArray.length; $page = $($('.page')[$('.page').length - 1]); //獲取當前列表中顯示的最后一頁 nextRemoveArray.push($page[0].outerHTML); //將最后一頁內容數組 $list.prepend(preRemoveArray.pop(length - 1)); //將上一頁內容添加進列表 document.body.scrollTop = document.body.scrollTop + $($('.page')[0]).height(); //設置滾動條位置 $page.remove(); //移除最后一頁 } } else if (isBottom()) { $page = $($('.page')[0]); //獲取當前列表中顯示的第一頁 preRemoveArray.push($page[0].outerHTML); //將第一頁內容數組 if (nextRemoveArray[0]) { //如果已經瀏覽過下面的內容 length = nextRemoveArray.length; $list.append(nextRemoveArray.pop(length - 1)); //將下一頁內容添加進列表 } else { //如果沒有瀏覽過下面的內容 renderSinglePage(); } document.body.scrollTop = document.body.scrollTop - $page.height(); //設置滾動條位置 $page.remove(); //移除第一頁 } }; }; var renderData = function() { var innerHTML = ''; //我設定它永遠只顯示兩頁,所以一開始先加載兩頁數據出來 for (var i = 0; i < 2; i++) { innerHTML += getData(); } $list.append(innerHTML); }; var renderSinglePage = function() { var innerHTML = ''; innerHTML += getData(); $list.append(innerHTML); }; var getData = function() { var innerHTML = ''; innerHTML += '<div class="page page-' + PAGE + '">'; for (var i = 0; i < PAGESIZE; i++) { innerHTML += '<li>' + PAGE + '</li>'; //為方便看的清楚,我們給每行數據標記它是屬於第幾頁的 } innerHTML += '</div>'; PAGE++; return innerHTML; }; var isBottom = function() { return document.body.scrollTop + window.screen.height == document.body.clientHeight; }; var isTop = function() { return document.body.scrollTop === 0; }; init();
PS: 為了方便開發,上面的代碼引入了zepto.js