移動端翻頁插件dropload.js(支持Zepto和jQuery)
dropload.js提供了最基本的上拉翻頁,下拉刷新功能。對于由服務端一次返回所有數(shù)據的情況基本通用。
但是,需求往往不是服務端一次性返回所有數(shù)據,往往還要支持服務端分頁,搜索,排序,多條件篩選等功能。(比較類似美團美食的界面)
一、解決方案
改進1:由于有分頁,搜索,排序,多條件篩選功能,可能都不需要上拉,進到頁面就沒有數(shù)據。
例如:搜索一個服務端不存在的名字。
所以,添加接口設置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:由以上問題還引發(fā)了一個bug,選擇不同的篩選條件,然后上拉加載更多,此時沒有反應了。
原因較復雜,舉例說明:選擇不同的篩選條件,數(shù)據量不一樣,如果不執(zhí)行resetload,那么頁面的的上拉計算距離就存在問題。
1. 只要發(fā)API到服務端,無論返回成功失敗,都必須執(zhí)行resetload,成功時需要在加載完全部新增的數(shù)據后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條數(shù)據,上拉加載更多返回10條數(shù)據,此時設置setHasData(false)。選擇篩選條件2,返回20條數(shù)據,上拉加載,你會驚奇的發(fā)現(xiàn)拉不動了。
why: setHasData(false)之后狀態(tài)還停留在沒有更多數(shù)據的狀態(tài)。此時應該鎖定了上拉加載,更改篩選條件后,沒有解除鎖定,所以不能上拉加載了。
解決方法:每次更改搜索條件,篩選條件,排序等時,都需要設置setHasData(true)。
二、調用方法
整體頁面邏輯較復雜。這里在整體解釋一遍。
1. 選擇要上拉加載的DIV,添加調用方法。
注意事項:
(1)記得保存返回對象。
(2)LoadDownFn時上拉加載后的回調,這里自己要處理的邏輯。我這里時翻頁發(fā)API,API參數(shù)中offset加20,然后發(fā)API。
(3)無論API返回失敗成功,都必須resetload。
這里強調:
fetchData函數(shù)調用發(fā)API,失敗或者成功都必須self.moreFund.resetload()。
并且失敗時直接調用self.moreFund.resetload()即可。成功時要在新的數(shù)據返回后,要先用JS動態(tài)加載HTML,加載完成后在執(zhí)行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="images/dropload_up.png"><span>上拉加載更多</span></div>',
domLoad: '<div class="dropload-load"><img class="loading-icon" src="images/droploading.gif"></div>',
domNoData: ''
},
loadDownFn: function() {
self.apiParams.offset += 20;
self.fetchData(true);
}
});
2. setHasData詳解
(1)什么時候需要設置true。
當非翻頁API觸發(fā)之前。即選擇新的篩選條件,選擇新的搜索字段,選擇新的排序字段。這個時候必須setHasData(true)。
this.moreFund.setHasData(true);
(2)什么時候設置false。
服務端返回數(shù)據后,比較服務端返回的條目數(shù)與API發(fā)送的條目數(shù)是否一致,不一致設置setHasData(false)。
if (data.length < this.apiParams.count){
this.moreFund.setHasData(false);
this.moreFund.lock();
}
3. lock與unlock詳解
(1)什么時候設置lock。
服務端返回數(shù)據后,比較服務端返回的條目數(shù)與API發(fā)送的條目數(shù)是否一致,不一致設置lock()。
當前頁面狀態(tài)不需要上拉加載時需要設置lock()。例如:在搜索框輸入的狀態(tài)。
(2)什么時候設置unlock。
只有一個地方需要調用。發(fā)送API之前設置unlock。
if (self.moreFund) {
self.moreFund.unlock();
}
三、JS和CSS源代碼
js:
(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>暫無數(shù)據</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);
CSS:
.dropload-up,
.dropload-down {
background-color: #F0EFF5;
position: relative;
height: 0;
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: 0;
line-height: 50px;
text-align: center;
}
.dropload-down .dropload-refresh,
.dropload-down .dropload-update,
.dropload-down .dropload-load {
top: 0;
bottom: auto;
}
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
underscore之Chaining_動力節(jié)點Java學院整理
本文通過文字說明與代碼的形式給大家介紹了underscore之Chaining的相關知識,感興趣的朋友一起學習吧2017-07-07
JS Excel讀取和寫入操作(模板操作)實現(xiàn)代碼
前一段時間一直在做報表,所以肯定會用到Excel的操作,但是在網上查閱資料有關JS操作excel較少,有的話,也都是老生常談或很零碎的一些東西。2010-04-04
鼠標移到div,浮層顯示明細,彈出層與div的上邊距左邊距重合(示例代碼)
這篇文章主要介紹了鼠標移到div,浮層顯示明細,彈出層與div的上邊距左邊距重合的實例代碼。需要的朋友可以過來參考下,希望對大家有所幫助2013-12-12
JavaScript使用原型和原型鏈實現(xiàn)對象繼承的方法詳解
這篇文章主要介紹了JavaScript使用原型和原型鏈實現(xiàn)對象繼承的方法,簡單講述了javascript原型與原型鏈的原理,并結合實例形式詳細分析了javascript中對象繼承的常見實現(xiàn)技巧,需要的朋友可以參考下2017-04-04

