在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元素的复用,其实彻底能够复用删除的元素做为将要添加的元素,只是变动其中的数据显示内容;
代码须要作相应的修改,就留给小伙伴们改进吧:)