一. 声明

  代码来源: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)的更多相关文章

  1. 移动端日历选择控件(支持Zepto和JQuery)

    移动端日历选择控件(支持Zepto和JQuery) <!DOCTYPE html> <html> <head> <meta charset="utf ...

  2. 移动端下拉刷新、加载更多插件dropload.js(基于jQuery/Zepto)

    移动端下拉刷新.加载更多插件dropload.js(基于jQuery/Zepto) 原文:http://www.grycheng.com/?p=1869 废话不多说,先让大家看一下案例效果: DEMO ...

  3. 用fiddler测试移动端翻页

    大家在移动端是怎么测试翻页的,肯定都是下拉或上滑吧,我也是这样测试的 但如果你要验证数据是否与pc端数据一致时,可能是第一页,第二页看看,或最后几页数据看看,在pc端看简单,直接点击最后一页就行,在移 ...

  4. 移动端-手机端-日历选择控件(支持Zepto和JQuery)

    一. 效果图 二. 功能说明 1. 支持切换年份,月份. 2. 支持点击选中日期,也可以点击确定选择日期. 三. 使用方法 1. 添加Input 在你的页面中添加Input输入框.可以再html里,也 ...

  5. 移动端下拉刷新、加载更多插件dropload.js(基于jQuery/Zepto)[转]

    使用方法 引用css和js <link rel="stylesheet" href="../dist/dropload.min.css"> < ...

  6. 移动端打印调试插件 - debug.js 介绍

    前文中我们学习过,用 Fiddler 作为代理可以在移动端打开本地的页面进行查看(如何用 fiddler 代理调试本地手机页面),但是对于 js 的调试却无能为力(需要借助其他调试手段,比如 UC浏览 ...

  7. 网站引导页插件intro.js 的用法

    intro.js是一个用于制作网页引导效果的js插件,用法很简单,intro.js.v2.0.rar 1.在需要的页面添加引用 intro.js introjs.css 这两个文件已经足够,但是文件夹 ...

  8. 移动端轮播图插件(支持Zepto和jQuery)

    一. 效果图 二. 功能介绍 1. 支持图片自动轮播和非自动轮播 2. 支持点击和滑动. 三. 简单介绍 代码都有注释,逻辑简单,不做更多赘述. 1. 在你的html中添加一行. <sectio ...

  9. Swipe-移动端触摸滑动插件swipe.js

    原文链接:http://caibaojian.com/swipe.html 插件特色 viaswipe.JS是一个比较有名的触摸滑动插件,它能够处理内容滑动,支持自定义选项,你可以让它自动滚动,控制滚 ...

随机推荐

  1. spring.net IOC容器

    spring.net 通过配置文件的方式 帮我们实现了IoC功能,实现方式非常灵活,且多种多样. 点击查看 创建对象 我们只需定义接口和实现方法,spring.net帮我们实现了其他功能. 第一步,定 ...

  2. 一起学CUDA(零)

    1.Nvidia为什么引入CUDA最近实验室已经有不少豪在入手本本了,因为学霸居多,所以大家一般都会说对显卡要求不高,不玩大型游戏,只是CPU不能差,Intel I7.3G的主频…… 其 实现在CPU ...

  3. 用Jquery Widgets Factory写自己的表格控件——AFGrid(支持增、删、改)

    一,Jquery Widget Factory介绍 官网地址 Demo:http://jqueryui.com/widget/ API:http://api.jqueryui.com/jQuery.w ...

  4. 开发者必读jQuery Mobile入门教程

    你每天都会对着它讲话,和它玩游戏,用它看新闻——没错,它就是你裤兜里的智能手机.android,黑莓还是iphone?为了让你清楚意识到究竟哪些才算是智能手机,我在下面总结了一个智能手机系统/设备的列 ...

  5. 【Unity入门】编辑器常用视图介绍

    版权声明:本文为博主原创文章,转载请注明出处. 打开Unity编辑器的主窗口,在窗口的右上角可以看到有个“Layout”按钮.这是用来对Unity编辑器主窗口上面的各个窗口面板进行布局的.通常情况下我 ...

  6. php获取上传多个文件缺失

    我们的一个页面编辑发布后台出现了图片无法上传保存的情况,经过调试对比发现,原来是file表单数量过多导致,减少file表单的数量即可上传成功.为了满足需求不减少file表单数并保证上传成功,于是更改了 ...

  7. 数往知来C#面向对象〈三〉

    C# 基础方法篇 一.复习 1)方法的重载 方法的重载并不是一个方法,实际上方法的重载就是一些不同的 方法,目的是为了方便程序员编码,所以将功能相近的方法命名相同 根据参数,编译器自动的去匹配方法体, ...

  8. linq性能剖析

    Orcas(VS2008&Framework3.5)给我们带来了很多令人兴奋的新特性,尤其是LINQ的引进,可以说方便了一大批开发 人员和框架设计人员.过去,当我们使用O/RMapping的一 ...

  9. Strassen算法

    如题,该算法是来自德国的牛逼的数学家strassen搞出来的,因为把n*n矩阵之间的乘法复杂度降低到n^(lg7)(lg的底是2),一开始想当然地认为朴素的做法是n^3,哪里还能有复杂度更低的做法,但 ...

  10. RSS阅读器&BT sync

    ①RSS阅读器? 答:RSS阅读器是一种软件或是说一个程序,这种软件可以自由读取RSS和Atom两种规范格式的文档,且这种读取RSS和Atom文档的软件有多个版本,由不同的人或公司开发,有着不同的名字 ...