移动端翻页插件dropload.js(支持Zepto和jQuery)
dropload.js提供了最基本的上拉翻页,下拉刷新功能。对于由服务端一次返回所有数据的情况基本通用。
但是,需求往往不是服务端一次性返回所有数据,往往还要支持服务端分页,搜索,排序,多条件筛选等功能。(比较类似美团美食的界面)
一、解决方案
改进1:由于有分页,搜索,排序,多条件筛选功能,可能都不需要上拉,进到页面就没有数据。
例如:搜索一个服务端不存在的名字。
所以,添加接口设置setHasData。
MyDropLoad.prototype.setHasData=function(ishasData){ varme=this; if(ishasData){ me.isData=true; me.$domDown.html(me.opts.domDown.domRefresh); fnRecoverContentHeight(me); }else{ me.isData=false; me.$domDown.html(me.opts.domDown.domNoData); fnRecoverContentHeight(me); } };
改进2:由以上问题还引发了一个bug,选择不同的筛选条件,然后上拉加载更多,此时没有反应了。
原因较复杂,举例说明:选择不同的筛选条件,数据量不一样,如果不执行resetload,那么页面的的上拉计算距离就存在问题。
1.只要发API到服务端,无论返回成功失败,都必须执行resetload,成功时需要在加载完全部新增的数据后resetload。
2.更改resetload如下,添加调用计算屏幕尺寸的方法。
MyDropLoad.prototype.resetload=function(){ varme=this; if(me.direction=='down'&&me.upInsertDOM){ me.$domUp.css({'height':'0'}).on('webkitTransitionEndmozTransitionEndtransitionend',function(){ me.loading=false; me.upInsertDOM=false; $(this).remove(); fnRecoverContentHeight(me); }); }elseif(me.direction=='up'){ me.loading=false; if(me.isData){ me.$domDown.html(me.opts.domDown.domRefresh); fnRecoverContentHeight(me); }else{ me.$domDown.html(me.opts.domDown.domNoData); } } }
3.解决以上两个问题,基本解决了90%的问题,还有一个是setHasData(false)之后的处理。(假设每页的count时20条)
bug:在筛序条件1:返回20条数据,上拉加载更多返回10条数据,此时设置setHasData(false)。选择筛选条件2,返回20条数据,上拉加载,你会惊奇的发现拉不动了。
why:setHasData(false)之后状态还停留在没有更多数据的状态。此时应该锁定了上拉加载,更改筛选条件后,没有解除锁定,所以不能上拉加载了。
解决方法:每次更改搜索条件,筛选条件,排序等时,都需要设置setHasData(true)。
二、调用方法
整体页面逻辑较复杂。这里在整体解释一遍。
1.选择要上拉加载的DIV,添加调用方法。
注意事项:
(1)记得保存返回对象。
(2)LoadDownFn时上拉加载后的回调,这里自己要处理的逻辑。我这里时翻页发API,API参数中offset加20,然后发API。
(3)无论API返回失败成功,都必须resetload。
这里强调:
fetchData函数调用发API,失败或者成功都必须self.moreFund.resetload()。
并且失败时直接调用self.moreFund.resetload()即可。成功时要在新的数据返回后,要先用JS动态加载HTML,加载完成后在执行self.moreFund.resetload()。
self.moreFund=$('#table-fundlist').dropload({ scrollArea:window, domDown:{ domClass:'dropload-down', domRefresh:'<divclass="dropload-refresh"><imgclass="drop-up-icon"src="images/dropload_up.png"><span>上拉加载更多</span></div>', domLoad:'<divclass="dropload-load"><imgclass="loading-icon"src="images/droploading.gif"></div>', domNoData:'' }, loadDownFn:function(){ self.apiParams.offset+=20; self.fetchData(true); } });
2.setHasData详解
(1)什么时候需要设置true。
当非翻页API触发之前。即选择新的筛选条件,选择新的搜索字段,选择新的排序字段。这个时候必须setHasData(true)。
this.moreFund.setHasData(true);
(2)什么时候设置false。
服务端返回数据后,比较服务端返回的条目数与API发送的条目数是否一致,不一致设置setHasData(false)。
if(data.length<this.apiParams.count){ this.moreFund.setHasData(false); this.moreFund.lock(); }
3.lock与unlock详解
(1)什么时候设置lock。
服务端返回数据后,比较服务端返回的条目数与API发送的条目数是否一致,不一致设置lock()。
当前页面状态不需要上拉加载时需要设置lock()。例如:在搜索框输入的状态。
(2)什么时候设置unlock。
只有一个地方需要调用。发送API之前设置unlock。
if(self.moreFund){ self.moreFund.unlock(); }
三、JS和CSS源代码
js:
(function($){ 'usestrict'; varwin=window; vardoc=document; var$win=$(win); var$doc=$(doc); $.fn.dropload=function(options){ returnnewMyDropLoad(this,options); }; varMyDropLoad=function(element,options){ varme=this; me.$element=$(element); me.upInsertDOM=false; me.loading=false; me.isLockUp=false; me.isLockDown=false; me.isData=true; me._scrollTop=0; me.init(options); }; MyDropLoad.prototype.init=function(options){ varme=this; me.opts=$.extend({},{ scrollArea:me.$element, domUp:{ domClass:'dropload-up', domRefresh:'<divclass="dropload-refresh"><imgclass="drop-down-icon"src="../images/dropload_down.png"><span>下拉刷新</span></div>', domUpdate:'<divclass="dropload-update"><imgclass="drop-up-icon"src="../images/dropload_up.png"><span>释放更新</span></div>', domLoad:'<divclass="dropload-load"><imgclass="loading-icon"src="../images/droploading.gif"></div>' }, domDown:{ domClass:'dropload-down', domRefresh:'<divclass="dropload-refresh"><imgclass="drop-up-icon"src="../images/dropload_up.png"><span>上拉加载更多</span></div>', domLoad:'<divclass="dropload-load"><imgclass="loading-icon"src="../images/droploading.gif"></div>', domNoData:'' //domNoData:'<divclass="dropload-noData"><span>暂无数据</span></div>' }, distance:50,//拉动距离 threshold:'',//提前加载距离 loadUpFn:'',//上方function loadDownFn:''//下方function },options); if(me.opts.loadDownFn!=''){ me.$element.append('<divclass="'+me.opts.domDown.domClass+'">'+me.opts.domDown.domRefresh+'</div>'); me.$domDown=$('.'+me.opts.domDown.domClass); } if(me.opts.scrollArea==win){ me.$scrollArea=$win; me._scrollContentHeight=$doc.height(); me._scrollWindowHeight=doc.documentElement.clientHeight; }else{ me.$scrollArea=me.opts.scrollArea; me._scrollContentHeight=me.$element[0].scrollHeight; me._scrollWindowHeight=me.$element.height(); } $win.on('resize',function(){ if(me.opts.scrollArea==win){ me._scrollWindowHeight=win.innerHeight; }else{ me._scrollWindowHeight=me.$element.height(); } }); me.$element.on('touchstart',function(e){ if(!me.loading){ fnTouches(e); fnTouchstart(e,me); } }); me.$element.on('touchmove',function(e){ if(!me.loading){ fnTouches(e,me); fnTouchmove(e,me); } }); me.$element.on('touchend',function(){ if(!me.loading){ fnTouchend(me); } }); me.$scrollArea.on('scroll',function(){ me._scrollTop=me.$scrollArea.scrollTop(); fnRecoverContentHeight(me) if(me.opts.threshold===''){ me._threshold=Math.floor(me.$domDown.height()*1/3); }else{ me._threshold=me.opts.threshold; } if(me.opts.loadDownFn!=''&&!me.loading&&!me.isLockDown&&me._threshold!=0&&(me._scrollContentHeight-me._threshold)<=(me._scrollWindowHeight+me._scrollTop)){ fnLoadDown(); } }); functionfnLoadDown(){ me.direction='up'; me.$domDown.html(me.opts.domDown.domLoad); me.loading=true; me.opts.loadDownFn(me); } }; functionfnTouches(e){ if(!e.touches){ e.touches=e.originalEvent.touches; } } functionfnTouchstart(e,me){ me._startY=e.touches[0].pageY; me.touchScrollTop=me.$scrollArea.scrollTop(); } functionfnTouchmove(e,me){ me._curY=e.touches[0].pageY; me._moveY=me._curY-me._startY; if(me._moveY>0){ me.direction='down'; }elseif(me._moveY<0){ me.direction='up'; } var_absMoveY=Math.abs(me._moveY); if(me.opts.loadUpFn!=''&&me.touchScrollTop<=0&&me.direction=='down'&&!me.isLockUp){ e.preventDefault(); me.$domUp=$('.'+me.opts.domUp.domClass); if(!me.upInsertDOM){ me.$element.prepend('<divclass="'+me.opts.domUp.domClass+'"></div>'); me.upInsertDOM=true; } fnTransition(me.$domUp,0); if(_absMoveY<=me.opts.distance){ me._offsetY=_absMoveY; me.$domUp.html(me.opts.domUp.domRefresh); }elseif(_absMoveY>me.opts.distance&&_absMoveY<=me.opts.distance*2){ me._offsetY=me.opts.distance+(_absMoveY-me.opts.distance)*0.5; me.$domUp.html(me.opts.domUp.domUpdate); }else{ me._offsetY=me.opts.distance+me.opts.distance*0.5+(_absMoveY-me.opts.distance*2)*0.2; } me.$domUp.css({'height':me._offsetY}); } } //touchend functionfnTouchend(me){ var_absMoveY=Math.abs(me._moveY); if(me.opts.loadUpFn!=''&&me.touchScrollTop<=0&&me.direction=='down'&&!me.isLockUp){ fnTransition(me.$domUp,300); if(_absMoveY>me.opts.distance){ me.$domUp.css({'height':me.$domUp.children().height()}); me.$domUp.html(me.opts.domUp.domLoad); me.loading=true; me.opts.loadUpFn(me); }else{ me.$domUp.css({'height':'0'}).on('webkitTransitionEndtransitionend',function(){ me.upInsertDOM=false; $(this).remove(); }); } me._moveY=0; } } //重新获取文档高度 functionfnRecoverContentHeight(me){ if(me.opts.scrollArea==win){ me._scrollContentHeight=$doc.height(); }else{ me._scrollContentHeight=me.$element[0].scrollHeight; } } MyDropLoad.prototype.lock=function(direction){ varme=this; if(direction===undefined){ if(me.direction=='up'){ me.isLockDown=true; }elseif(me.direction=='down'){ me.isLockUp=true; }else{ me.isLockUp=true; me.isLockDown=true; } }elseif(direction=='up'){ me.isLockUp=true; }elseif(direction=='down'){ me.isLockDown=true; } }; MyDropLoad.prototype.unlock=function(){ varme=this; me.isLockUp=false; me.isLockDown=false; }; MyDropLoad.prototype.setHasData=function(ishasData){ varme=this; if(ishasData){ me.isData=true; me.$domDown.html(me.opts.domDown.domRefresh); fnRecoverContentHeight(me); }else{ me.isData=false; me.$domDown.html(me.opts.domDown.domNoData); fnRecoverContentHeight(me); } }; MyDropLoad.prototype.resetload=function(){ varme=this; if(me.direction=='down'&&me.upInsertDOM){ me.$domUp.css({'height':'0'}).on('webkitTransitionEndmozTransitionEndtransitionend',function(){ me.loading=false; me.upInsertDOM=false; $(this).remove(); fnRecoverContentHeight(me); }); }elseif(me.direction=='up'){ me.loading=false; if(me.isData){ me.$domDown.html(me.opts.domDown.domRefresh); fnRecoverContentHeight(me); }else{ me.$domDown.html(me.opts.domDown.domNoData); } } }; functionfnTransition(dom,num){ dom.css({ '-webkit-transition':'all'+num+'ms', 'transition':'all'+num+'ms' }); } })(window.Zepto||window.jQuery);
CSS:
.dropload-up, .dropload-down{ background-color:#F0EFF5; position:relative; height:0; overflow:hidden; } .dropload-down{ height:50px; border-top:1pxsolid#e5e5e5; } .dropload-refresh.drop-up-icon, .dropload-refresh.drop-down-icon, .dropload-update.drop-up-icon, .dropload-update.drop-down-icon{ vertical-align:text-bottom; margin-right:3px; height:16px; width:12px; } .dropload-load.loading-icon{ vertical-align:middle; height:20px; width:20px; } .dropload-refreshspan, .dropload-updatespan{ vertical-align:middle; line-height:18px; font-size:16px; color:#585858; } .dropload-noData{ border-bottom:1pxsolid#e5e5e5; background-color:#FFFFFF; } .dropload-noDataspan{ line-height:18px; font-size:14px; color:#999999; } .dropload-refresh, .dropload-update, .dropload-load, .dropload-noData{ position:absolute; width:100%; height:50px; bottom:0; line-height:50px; text-align:center; } .dropload-down.dropload-refresh, .dropload-down.dropload-update, .dropload-down.dropload-load{ top:0; bottom:auto; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。