移动端翻页插件dropload.js(支持Zepto和jQuery)
一. 声明
代码来源:github上的dropload项目。
二. 问题
dropload.js提供了最基本的上拉翻页,下拉刷新功能。对于由服务端一次返回所有数据的情况基本通用。
但是,需求往往不是服务端一次性返回所有数据,往往还要支持服务端分页,搜索,排序,多条件筛选等功能。(比较类似美团美食的界面)
三. 解决方案。
改进1:由于有分页,搜索,排序,多条件筛选功能,可能都不需要上拉,进到页面就没有数据。
例如:搜索一个服务端不存在的名字。
所以,添加接口设置setHasData。
MyDropLoad.prototype.setHasData = function(ishasData) {
var me = 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() {
var me = this;
if (me.direction == 'down' && me.upInsertDOM) {
me.$domUp.css({ 'height': '0' }).on('webkitTransitionEnd mozTransitionEnd transitionend', function() {
me.loading = false;
me.upInsertDOM = false;
$(this).remove();
fnRecoverContentHeight(me);
});
} else if (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: '<div class="dropload-refresh"><img class="drop-up-icon" src="data:images/dropload_up.png"><span>上拉加载更多</span></div>',
domLoad: '<div class="dropload-load"><img class="loading-icon" src="data: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源代码
;
(function($) {
'use strict';
var win = window;
var doc = document;
var $win = $(win);
var $doc = $(doc);
$.fn.dropload = function(options) {
return new MyDropLoad(this, options);
};
var MyDropLoad = function(element, options) {
var me = 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) {
var me = this;
me.opts = $.extend({}, {
scrollArea: me.$element,
domUp: {
domClass: 'dropload-up',
domRefresh: '<div class="dropload-refresh"><img class="drop-down-icon" src="../images/dropload_down.png"><span>下拉刷新</span></div>',
domUpdate: '<div class="dropload-update"><img class="drop-up-icon" src="../images/dropload_up.png"><span>释放更新</span></div>',
domLoad: '<div class="dropload-load"><img class="loading-icon" src="../images/droploading.gif"></div>'
},
domDown: {
domClass: 'dropload-down',
domRefresh: '<div class="dropload-refresh"><img class="drop-up-icon" src="../images/dropload_up.png"><span>上拉加载更多</span></div>',
domLoad: '<div class="dropload-load"><img class="loading-icon" src="../images/droploading.gif"></div>',
domNoData: ''
//domNoData : '<div class="dropload-noData"><span>暂无数据</span></div>'
},
distance: 50, // 拉动距离
threshold: '', // 提前加载距离
loadUpFn: '', // 上方function
loadDownFn: '' // 下方function
}, options); if (me.opts.loadDownFn != '') {
me.$element.append('<div class="' + 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();
}
}); function fnLoadDown() {
me.direction = 'up';
me.$domDown.html(me.opts.domDown.domLoad);
me.loading = true;
me.opts.loadDownFn(me);
}
}; function fnTouches(e) {
if (!e.touches) {
e.touches = e.originalEvent.touches;
}
} function fnTouchstart(e, me) {
me._startY = e.touches[0].pageY;
me.touchScrollTop = me.$scrollArea.scrollTop();
} function fnTouchmove(e, me) {
me._curY = e.touches[0].pageY;
me._moveY = me._curY - me._startY; if (me._moveY > 0) {
me.direction = 'down';
} else if (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('<div class="' + 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);
} else if (_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
function fnTouchend(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('webkitTransitionEnd transitionend', function() {
me.upInsertDOM = false;
$(this).remove();
});
}
me._moveY = 0;
}
} // 重新获取文档高度
function fnRecoverContentHeight(me) {
if (me.opts.scrollArea == win) {
me._scrollContentHeight = $doc.height();
} else {
me._scrollContentHeight = me.$element[0].scrollHeight;
}
} MyDropLoad.prototype.lock = function(direction) {
var me = this;
if (direction === undefined) {
if (me.direction == 'up') {
me.isLockDown = true;
} else if (me.direction == 'down') {
me.isLockUp = true;
} else {
me.isLockUp = true;
me.isLockDown = true;
}
} else if (direction == 'up') {
me.isLockUp = true;
} else if (direction == 'down') {
me.isLockDown = true;
}
}; MyDropLoad.prototype.unlock = function() {
var me = this;
me.isLockUp = false;
me.isLockDown = false;
}; MyDropLoad.prototype.setHasData = function(ishasData) {
var me = 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() {
var me = this;
if (me.direction == 'down' && me.upInsertDOM) {
me.$domUp.css({ 'height': '0' }).on('webkitTransitionEnd mozTransitionEnd transitionend', function() {
me.loading = false;
me.upInsertDOM = false;
$(this).remove();
fnRecoverContentHeight(me);
});
} else if (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);
}
}
}; function fnTransition(dom, num) {
dom.css({
'-webkit-transition': 'all ' + num + 'ms',
'transition': 'all ' + num + 'ms'
});
}
})(window.Zepto || window.jQuery);
.dropload-up,
.dropload-down {
background-color: #F0EFF5;
position: relative;
height:;
overflow: hidden;
} .dropload-down {
height: 50px;
border-top: 1px solid #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-refresh span,
.dropload-update span {
vertical-align: middle;
line-height: 18px;
font-size: 16px;
color: #585858;
} .dropload-noData {
border-bottom: 1px solid #e5e5e5;
background-color: #FFFFFF;
} .dropload-noData span {
line-height: 18px;
font-size: 14px;
color: #999999;
} .dropload-refresh,
.dropload-update,
.dropload-load,
.dropload-noData {
position: absolute;
width: 100%;
height: 50px;
bottom:;
line-height: 50px;
text-align: center;
} .dropload-down .dropload-refresh,
.dropload-down .dropload-update,
.dropload-down .dropload-load {
top:;
bottom: auto;
}
移动端翻页插件dropload.js(支持Zepto和jQuery)的更多相关文章
- 移动端日历选择控件(支持Zepto和JQuery)
移动端日历选择控件(支持Zepto和JQuery) <!DOCTYPE html> <html> <head> <meta charset="utf ...
- 移动端下拉刷新、加载更多插件dropload.js(基于jQuery/Zepto)
移动端下拉刷新.加载更多插件dropload.js(基于jQuery/Zepto) 原文:http://www.grycheng.com/?p=1869 废话不多说,先让大家看一下案例效果: DEMO ...
- 用fiddler测试移动端翻页
大家在移动端是怎么测试翻页的,肯定都是下拉或上滑吧,我也是这样测试的 但如果你要验证数据是否与pc端数据一致时,可能是第一页,第二页看看,或最后几页数据看看,在pc端看简单,直接点击最后一页就行,在移 ...
- 移动端-手机端-日历选择控件(支持Zepto和JQuery)
一. 效果图 二. 功能说明 1. 支持切换年份,月份. 2. 支持点击选中日期,也可以点击确定选择日期. 三. 使用方法 1. 添加Input 在你的页面中添加Input输入框.可以再html里,也 ...
- 移动端下拉刷新、加载更多插件dropload.js(基于jQuery/Zepto)[转]
使用方法 引用css和js <link rel="stylesheet" href="../dist/dropload.min.css"> < ...
- 移动端打印调试插件 - debug.js 介绍
前文中我们学习过,用 Fiddler 作为代理可以在移动端打开本地的页面进行查看(如何用 fiddler 代理调试本地手机页面),但是对于 js 的调试却无能为力(需要借助其他调试手段,比如 UC浏览 ...
- 网站引导页插件intro.js 的用法
intro.js是一个用于制作网页引导效果的js插件,用法很简单,intro.js.v2.0.rar 1.在需要的页面添加引用 intro.js introjs.css 这两个文件已经足够,但是文件夹 ...
- 移动端轮播图插件(支持Zepto和jQuery)
一. 效果图 二. 功能介绍 1. 支持图片自动轮播和非自动轮播 2. 支持点击和滑动. 三. 简单介绍 代码都有注释,逻辑简单,不做更多赘述. 1. 在你的html中添加一行. <sectio ...
- Swipe-移动端触摸滑动插件swipe.js
原文链接:http://caibaojian.com/swipe.html 插件特色 viaswipe.JS是一个比较有名的触摸滑动插件,它能够处理内容滑动,支持自定义选项,你可以让它自动滚动,控制滚 ...
随机推荐
- a标签包input引起的问题
最近公司中的一个项目中,有一个同事跟我说,他写的输入框不能选中输入内容了,并且光标也不能插入到已写好的文字其他位置. 简化了一下他的代码结构,如下: <a href="javascri ...
- 【解题报告】[动态规划] RQNOJ - PID15 / 采药
原题地址:http://www.rqnoj.cn/problem/15 好久以前做的题了,是个背包问题,就不解释了. #include<stdio.h> #define MAX 100 i ...
- 【转】让Souce Insight支持多种语言的语法高亮:Python,Ruby,ARM汇编,windows脚本文件(bat/batch),PPC,SQL,TCL,Delphi等
原文网址:http://www.crifan.com/source_insight_support_highlight_for_python_ruby_arm_batch_ppc_sql_tcl_de ...
- 【转】MIPS交叉编译环境的建立
原文网址:http://imgtec.eetrend.com/forum/2371 我觉得对于MIPS处理起来说最令新手头疼的应该就是编译环境的建立了,这点MIPS做的确实不是很好,不像ARM那样有许 ...
- swfupload 参数说明
一.配置参数对象中的常用属性及说明 属性 类型 默认值 描述 upload_url String 处理上传文件的服务器端页面的url地址,可以是绝对地址,也可以是相对地址,当为相对地址时相对的是当 ...
- ArcGIS Engine 捕捉
原文 ArcGIS Engine 捕捉 bool bCreateElement = true; ;//时间间隔 ;//初始值 IElement m_element = null; //界面绘制点元素 ...
- 数学概念 z
数学是很难的科学,但因为它是科学家用数学来解释宇宙的语言,我们无可避免的要学习它.看看下面的这些 GIF 动图,它们提供了视觉的方式来帮助你理解各种数学技巧. 1.椭圆的画法 2.杨辉三角问题(Pas ...
- 性能测试之Windows常见性能计数器
性能计数器(counter)是描述服务器或操作系统性能的一些数据指标.计数器在性能测试中发挥着“监控和分析”的关键作用,尤其是在分析系统的可扩展性.进行性能瓶颈的定位时,对计数器的取值的分析非常关键. ...
- 生产环境服务CPU高问题分析
问题描述: 现网个别时候会出现CPU突然飙高的现象,飙高后不能恢复正常. 分析过程: CPU飙高后抓dump,最好本机看,其它机器看dump可能需要下载服务运行机器的sos,clr 0:000 ...
- python中的静态方法和类方法
在python中,各种方法的定义如下所示: class MyClass(object): #在类中定义普通方法,在定义普通方法的时候,必须添加self def foo(self,x): print & ...