在HTML開發頁面工程中,常常會遇到滾動列表-當實際須要顯示的內容寬度或高度超過容器的寬度或高度時,設置CSScss
overflow-x:auto; overfow-y:auto;
當滾動列表中的內容比較少時,咱們能夠一次性加載全部的內容到列表容器中顯示。
當滾動列表中的內容比較多,使用分頁加載的方式逐步加載數據,2種方式html
1.經過在列表的末尾添加一個標別元素indicator,和添加列表的scroll事件來監聽indicator元素是否可見,若是可見那麼提交請求加載下一頁數據,append到列表內容的尾部。經過這個方式能夠實現數據的無限加載,一直到數據取完。
2.在列表下部添加分頁工具條,用戶經過請求獲取指定頁的數據而且替換到當前列表裏的內容
jquery
對於方式1對用戶想對友好,加載過的數據不會重複加載,請求的資源少,可是當量多時,頁面上的DOM元素量很不斷增長,消耗的內存也會加大
方式2用戶每翻一頁都要從新請求數據,即便對於翻過看過的數據想要從新看也是如此,請求資源多,好處是頁面DOM元素數量固定,佔用內存資源少app
那麼有沒有一種方式結合方式1和方式2的優勢,摒棄缺點,融合優化下呢?ide
解決方案:
對當前列表的寬高固定,對列表包含的內容高度固定,列表滾動知足條件時動態刪除或添加元素,保持元素數量的固定已經內容在列表可視區域的正確顯示。函數
列表可以無限滾動,數據可以無限加載,DOM元素保持必定數量工具
先放出實現的代碼吧優化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="libs/jquery.js"></script> <style> .infinity-scroll{ width: 300px; height: 500px; position: absolute; left:10px; top:10px; background-color: #ffffff; box-sizing: border-box; border:1px solid green; overflow-x: hidden; overflow-y: auto; } .main-content{ position: absolute; left: 0px; top: 0px; width: 100%; box-sizing: border-box; background-color: #cccccc; border: 1px solid #ffffff; } .item{ width: 100%; height: 50px; background-color: #ffffff; } .item:nth-child(2n+1){ background-color: green; } .scroll-wrapper{ width: 100%; background-color: red; height: 2000px; } .hide{ display: none; } </style> <script> $(function(){ var infinity_scroll_height; var infinity_scroll_top; var isLoading=false; var lastMaxItemIndex=parseInt($(".item.last-item").last().attr("data-items-index"),10); function appendNewItems(prev_main_content_height,scroll_top){ requestAnimationFrame(function(){ var documentFragment=document.createDocumentFragment(); var i,item; var lastIndex=parseInt($(".item.last-item").attr("data-items-index"),10); $(".item.last-item").removeClass("last-item"); for(i=0;i<10;i++){ lastIndex++; item=$('<div class="item" data-items-index="0">2</div>'); $(item).attr("data-items-index",(lastIndex)); $(item).html(lastIndex+1); if(i===9){ $(item).addClass("last-item"); } documentFragment.appendChild($(item)[0]); } //更新此字段,做爲是否 須要更新main-content的 height 的標誌 //lastMaxItemIndex=Math.max(lastMaxItemIndex,lastIndex); //同時removefirst-item後面的10個元素 $(".item").slice(0,10).remove(); $(".item").first().addClass("first-item"); var mainContentPaddingTop=$(".main-content").css("padding-top"); mainContentPaddingTop=parseFloat(mainContentPaddingTop.split("px")[0]||0); $(".main-content").append(documentFragment); if(lastMaxItemIndex<lastIndex){ $(".main-content").css({ "height":prev_main_content_height+10*50, "padding-top":mainContentPaddingTop+10*50 }); lastMaxItemIndex=lastIndex; }else{ $(".main-content").css({ "padding-top":mainContentPaddingTop+10*50 }); } $('.infinity-scroll').off("scroll",infinity_scrollFun); $('.infinity-scroll').scrollTop(scroll_top); $('.infinity-scroll').on("scroll",infinity_scrollFun); isLoading=false; }); } function restorePreItems(prev_main_content_height,scroll_top){ requestAnimationFrame(function(){ //刪除底部的10個元素 var documentFragment=document.createDocumentFragment(); var i,item,mainContentPaddingTop; var lastIndex=parseInt($(".item.first-item").attr("data-items-index"),10); console.log(".item.first-item lastIndex%s",lastIndex); if(lastIndex<=0){ isLoading=false; return ; } $(".item.first-item").removeClass("first-item"); for(i=0;i<10;i++){ // lastIndex--; item=$('<div class="item" data-items-index="0"></div>'); $(item).attr("data-items-index",(lastIndex-10+i)); $(item).html(lastIndex-10+i+1); if(i===0){ $(item).addClass("first-item"); } documentFragment.appendChild($(item)[0]); } //刪除尾部的10個元素 $(".item.last-item").prevAll().slice(0,9).remove(); $(".item.last-item").remove(); $(".item").last().addClass("last-item"); mainContentPaddingTop=$(".main-content").css("padding-top"); mainContentPaddingTop=parseFloat(mainContentPaddingTop.split("px")[0]||0); $(".main-content").css({ "padding-top":mainContentPaddingTop-10*50 }); $(".main-content").prepend(documentFragment); $('.infinity-scroll').off("scroll",infinity_scrollFun); $('.infinity-scroll').scrollTop(scroll_top); $('.infinity-scroll').on("scroll",infinity_scrollFun); isLoading=false; //頭部添加新的10個元素 }); } function infinity_scrollFun(){ var last_item_top,first_item_top; if(!infinity_scroll_height){ infinity_scroll_height=$(".infinity-scroll")[0].getBoundingClientRect().height; infinity_scroll_top=$(".infinity-scroll")[0].getBoundingClientRect().top; } if(isLoading){ return; }else{ last_item_top=$(".item.last-item")[0].getBoundingClientRect().top; last_item_top=last_item_top-infinity_scroll_top; } console.log("~~~last_item_top=%s, infinity_scroll_height%s~~~",last_item_top,infinity_scroll_height); if(last_item_top<=infinity_scroll_height){ isLoading=true; appendNewItems($(".main-content")[0].getBoundingClientRect().height,$(this).scrollTop()); return; } first_item_top=$(".item.first-item")[0].getBoundingClientRect().top; first_item_top=first_item_top-infinity_scroll_top; console.log("~~first_item_top~~~%s",first_item_top); if(first_item_top>=0){ restorePreItems($(".main-content")[0].getBoundingClientRect().height,$(this).scrollTop()); return; } console.log("~~~scrollTop~~~"+$(this).scrollTop()); } $('.infinity-scroll').on("scroll",infinity_scrollFun); }); </script> </head> <body> <div class="infinity-scroll"> <div class="main-content"> <div class="item first-item" data-items-index="0">1</div> <div class="item" data-items-index="1">2</div> <div class="item" data-items-index="2">3</div> <div class="item" data-items-index="3">4</div> <div class="item" data-items-index="4">5</div> <div class="item" data-items-index="5">6</div> <div class="item" data-items-index="6">7</div> <div class="item" data-items-index="7">8</div> <div class="item" data-items-index="8">9</div> <div class="item " data-items-index="9">10</div> <div class="item " data-items-index="10">11</div> <div class="item" data-items-index="11">12</div> <div class="item" data-items-index="12">13</div> <div class="item" data-items-index="13">14</div> <div class="item" data-items-index="14">15</div> <div class="item" data-items-index="15">16</div> <div class="item" data-items-index="16">17</div> <div class="item" data-items-index="17">18</div> <div class="item" data-items-index="18">19</div> <div class="item " data-items-index="19">20</div> <div class="item " data-items-index="20">21</div> <div class="item" data-items-index="21">22</div> <div class="item" data-items-index="22">23</div> <div class="item" data-items-index="23">24</div> <div class="item" data-items-index="24">25</div> <div class="item" data-items-index="25">26</div> <div class="item" data-items-index="26">27</div> <div class="item" data-items-index="27">28</div> <div class="item" data-items-index="28">29</div> <div class="item last-item" data-items-index="29">30</div> </div> </div> </body> </html>
.infinity-scroll
指定列表容器,CSS設置其水平不滾動,垂直方向自動滾動.main-content
爲滾動內容組件,其高度隨着實際內容的增多而加大.item
爲滾動內容的單個項目元素.first-item
爲當前放置在列表容器內的內容的第1個元素,同時爲當前滾動到頂部的標識元素.last-item
爲當前放置在列表容器內的內容的最後一個元素,同時爲當前滾動到底部的標識元素this
放3組元素做爲初始的列表內容,爲何是3組呢?爲了保證滾動的流暢性,當前列表窗口顯示一組,捲起一組,下部隱藏一組rest
$('.infinity-scroll').on("scroll",infinity_scrollFun);
爲列表添加scroll事件監聽infinity_scrollFun
infinity_scrollFun函數
計算列表容器的高度和離viewpoint頂部距離
if(!infinity_scroll_height){ infinity_scroll_height=$(".infinity-scroll")[0].getBoundingClientRect().height; infinity_scroll_top=$(".infinity-scroll")[0].getBoundingClientRect().top; }
計算.last-item
的位置,若是正在加載數據不執行任何動做
if(isLoading){ return; }else{ last_item_top=$(".item.last-item")[0].getBoundingClientRect().top; last_item_top=last_item_top-infinity_scroll_top; }
若是.last-item
出如今列表窗口,那麼加載新的數據
if(last_item_top<=infinity_scroll_height){ isLoading=true; appendNewItems($(".main-content")[0].getBoundingClientRect().height,$(this).scrollTop()); return; }
若是.first-item
出如今列表窗口,那麼restore原來已經加載過的數據
first_item_top=$(".item.first-item")[0].getBoundingClientRect().top; first_item_top=first_item_top-infinity_scroll_top; console.log("~~first_item_top~~~%s",first_item_top); if(first_item_top>=0){ restorePreItems($(".main-content")[0].getBoundingClientRect().height,$(this).scrollTop()); return; }
appendNewItems函數
保持整體元素的個數,將.first-item
後的10個元素刪除,添加新的內容到底部,並要保持內容的實際高度及顯示在列表窗口的內容的位置
$(".item").slice(0,10).remove(); $(".item").first().addClass("first-item"); var mainContentPaddingTop=$(".main-content").css("padding-top"); mainContentPaddingTop=parseFloat(mainContentPaddingTop.split("px")[0]||0); $(".main-content").append(documentFragment); if(lastMaxItemIndex<lastIndex){ $(".main-content").css({ "height":prev_main_content_height+10*50, "padding-top":mainContentPaddingTop+10*50 }); lastMaxItemIndex=lastIndex; }else{ $(".main-content").css({ "padding-top":mainContentPaddingTop+10*50 }); } //放置在設置scrollTop時頁面滾動,先移除scroll事件監聽,而後再添加監聽 $('.infinity-scroll').off("scroll",infinity_scrollFun); $('.infinity-scroll').scrollTop(scroll_top); $('.infinity-scroll').on("scroll",infinity_scrollFun);
restorePreItems函數
功能和appendNewItems函數相似,只是刪除底部的10個元素,添加10個元素到頭部,並要保持內容的實際高度及顯示在列表窗口的內容的位置
//刪除尾部的10個元素 $(".item.last-item").prevAll().slice(0,9).remove(); $(".item.last-item").remove(); $(".item").last().addClass("last-item"); mainContentPaddingTop=$(".main-content").css("padding-top"); mainContentPaddingTop=parseFloat(mainContentPaddingTop.split("px")[0]||0); $(".main-content").css({ "padding-top":mainContentPaddingTop-10*50 }); $(".main-content").prepend(documentFragment); $('.infinity-scroll').off("scroll",infinity_scrollFun); $('.infinity-scroll').scrollTop(scroll_top); $('.infinity-scroll').on("scroll",infinity_scrollFun);
上面的假設內容區的item的高度都是相同,而且每次加在的item數量都是相同的。若是內容item的高度是動態變化的;
同時沒有作到頁面DOM元素的複用,其實徹底能夠複用刪除的元素做爲將要添加的元素,只是變動其中的數據顯示內容;
代碼須要作相應的修改,就留給小夥伴們改進吧:)