原生js之列表优化-无限滚动实例
2018-07-23 本文已影响561人
陨石坠灭
参考
参考给出的是一个字幕自动滚动的例子,不过这个例子有个坑,就是#rule
必须设置高度,否则的话,看不到滚动的效果。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>列表之无限滚动</title>
<style>
#rule{
max-height: 400px;
overflow-y: auto;
}
</style>
</head>
<body>
<div id="rule">
<div class="list" id="list">
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
<p>用户185****0000 获得XXX优惠券</p>
</div>
<div class="list2" id="list2"></div>
</div>
<script>
(function(){
var speed = 50;
var list = document.getElementById('list');
var list2 = document.getElementById('list2');
var rule = document.getElementById('rule');
list2.innerHTML = list.innerHTML;
function Marquee() {
if (list2.offsetTop - rule.scrollTop <= 0)
rule.scrollTop -= list.offsetHeight;
else {
rule.scrollTop++;
}
}
var MyMar = setInterval(Marquee, speed);
rule.addEventListener('mouseover',function () {
clearInterval(MyMar)
});
rule.addEventListener ('mouseout',function () {
MyMar = setInterval(Marquee, speed)
});
})();
</script>
</body>
</html>
作为一个例子,这个已经是非常棒的效果了,但是却不是想要的效果,我想要的效果是:下滑到底部时自动加载下一页,上滑时自动加载到上一页。
可能有人会问,这个还需要实现吗?答案:是的。
我们现在讲的其实是性能优化的问题,这里要用到元素重用的方法,并不是一次性将所有数据加载到页面。而是通过两个list动态地切换,从而实现无限滚动的效果。当然,这里的‘无限滚动’并不是一直可以滚动下去,在实际应用中,第一页将不能再上滑,最后一页将不能再下滑。
技术点
- 监听滑动事件
rule.addEventListener('scroll',function(){
...
})
- 判断是否滑动到了底部或者顶部
//判断是否滑动到了底部
var top = rule.scrollTop + rule.offsetHeight + rule.offsetTop;
if (list2.offsetTop + list2.offsetHeight <= top){
...
}
...
//判断是否滑动到了顶部
else if(rule.scrollTop <= 10){
...
}
- 滑动的方向
var recordTop = rule.scrollTop;
rule.addEventListener('scroll',function(){
if(rule.scrollTop - recordTop > 0){//向下滚动
...
}else if(rule.scrollTop - recordTop < 0){//向上滚动
...
}
...
recordTop = rule.scrollTop;
})
- 代码替换
在list于list2交替时,两部分代码需要做一下替换
var html = list.innerHTML;
list.innerHTML = list1.innerHTML;
list1.innerHTML = html;
- 定义状态 loadingUp,doneUp,loadingDown,doneDown,STATE_undo
const
STATE_loadingUp='loadingUp', //正在加载上一页数据
STATE_doneUp='doneUp',//完成上一页数据的加载
STATE_loadingDown='loadingDown',//正在加载下一页数据
STATE_doneDown='doneDown',//完成下一页数据的加载
STATE_undo='';//默认状态
由于数据需要在滑动到底部之前加载,所以需要定义状态,以免数据被重复加载。
- 分页
分页的话,这里用page
、pageSize
和total
来实现
完整案例
如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表之无限滚动</title>
<style>
#rule{
max-height: 400px;
overflow-y: auto;
padding: 0px;
margin: 0px;
}
#list{
/* background-color: gray; */
padding: 0px;
margin: 0px;
}
#list2{
/* background-color:lightgray; */
padding: 0px;
margin: 0px;
}
#vv, #gg{
width: 100%;
}
.line{
padding: 10px;
}
</style>
</head>
<body>
<input type="text" id="vv" readonly />
<input type="text" id="gg" readonly />
<div id="rule">
<div class="list" id="list">
</div>
<div class="list2" id="list2"></div>
</div>
<script>
(function(){
var speed = 50;
var list = document.getElementById('list');
var list2 = document.getElementById('list2');
var rule = document.getElementById('rule');
var con = document.getElementById('vv');
var con2 = document.getElementById('gg');
const
STATE_loadingUp='loadingUp',
STATE_doneUp='doneUp',
STATE_loadingDown='loadingDown',
STATE_doneDown='doneDown',
STATE_undo='';
var pageData = {
page:0,
pageSize:20,
items:[],
total:2,
state:'', //状态: loadingUp,doneUp,loadingDown,doneDown
recordTop : rule.scrollTop,
};
function createElements(ele,page){
var flag = ele.innerHTML ? true : false;
ele.innerHTML = '';
if(page >= pageData.total){
pageData.state = STATE_undo;
return;
}
var size = Math.min((page+1)*pageData.pageSize, pageData.items.length) - page*pageData.pageSize;
var num = page * pageData.pageSize;
for(var i = 0;i<size;i++){
ele.innerHTML += '<div class="line"><span>'+pageData.items[num+i]+'</span></div>';
}
ele.setAttribute('page', page);
if(!flag){
pageData.state = STATE_undo;
}else if(pageData.state == STATE_loadingDown){
pageData.state = STATE_doneDown;
}else if(pageData.state == STATE_loadingUp){
pageData.state = STATE_doneUp;
}
}
function replaceEle(e1, e2){
var html = e1.innerHTML;
e1.innerHTML = e2.innerHTML;
e2.innerHTML = html;
var page = e1.getAttribute('page');
e1.setAttribute('page',e2.getAttribute('page'));
e2.setAttribute('page',page);
}
//init
var size = 100;
for(var i = 0; i< size;i++){
pageData.items.push((i+1)+". 用户185****0000 获得XXX优惠券");
}
pageData.total = Math.ceil(size/pageData.pageSize);
// list2.innerHTML = list.innerHTML;
createElements(list,0);
createElements(list2,1);
rule.addEventListener('scroll',function(){
var data = {
list:{
offsetTop:list.offsetTop,
offsetHeight:list.offsetHeight,
},
list2:{
offsetTop:list2.offsetTop,
offsetHeight:list2.offsetHeight,
},
rule:{
scrollTop:rule.scrollTop,
offsetTop:rule.offsetTop,
offsetHeight:rule.offsetHeight,
}
};
con.value = JSON.stringify(data);
var data2 = {
list:list.offsetTop + list.offsetHeight,
list2:list2.offsetTop + list2.offsetHeight,
rule:rule.scrollTop + rule.offsetHeight + rule.offsetTop,
dir:rule.scrollTop - pageData.recordTop,
page:pageData.page,
state:pageData.state
};
con2.value = JSON.stringify(data2);
var top = rule.scrollTop + rule.offsetHeight + rule.offsetTop;
var offset = 200;
// if (list2.offsetTop - rule.scrollTop <= 0 && pageData.page < 0){//向下翻页
if ((list2.offsetTop + list2.offsetHeight <= top) && pageData.page + 2 < pageData.total && pageData.state == STATE_doneDown){//向下翻页
rule.scrollTop -= list.offsetHeight;
pageData.page++;
replaceEle(list,list2);
pageData.state = STATE_undo;
}else if(rule.scrollTop <= 10 && pageData.page > 0 && pageData.state == STATE_doneUp){//向上翻页
rule.scrollTop += list.offsetHeight;
pageData.page--;
replaceEle(list,list2);
pageData.state = STATE_undo;
}
else if(rule.scrollTop - pageData.recordTop > 0 && (list2.offsetTop + list2.offsetHeight <= top + offset)){//向下滚动 加载下一页数据
if(pageData.state == STATE_doneUp || pageData.state == STATE_loadingUp){
pageData.state = STATE_undo;
//TODO 去除掉事件
}
if(pageData.state == STATE_undo){//加载数据
pageData.state = STATE_loadingDown;
createElements(list,pageData.page+2)
}
}else if(rule.scrollTop - pageData.recordTop < 0 && rule.scrollTop <= offset){//向上滚动 加载上一页数据
if(pageData.state == STATE_doneDown || pageData.state == STATE_loadingDown){
pageData.state = STATE_undo;
//TODO 去除掉事件
}
if(pageData.page == 0){}
else if(pageData.state == STATE_undo){//加载数据
pageData.state = STATE_loadingUp;
if(!list.innerHTML){
createElements(list,pageData.page)
}else{
createElements(list2,pageData.page-1)
}
}
}else if(rule.scrollTop > offset && (list2.offsetTop + list2.offsetHeight > top + offset)){//矫正
var page1 = list.getAttribute('page');
var page2 = list2.getAttribute('page');
if(Math.abs(page1 - page2) != 1 || (page1 != pageData.page+1 && page2 != pageData.page+1)){
console.error(page1+","+page2+","+pageData.page);
createElements(list2,pageData.page+1);
pageData.state = STATE_undo;
}
}
pageData.recordTop = rule.scrollTop;
});
})();
</script>
</body>
</html>
提示
去掉#list
和#list2
的背景颜色的注释可以更加清晰的了解源代码,无限滚动的原理。