移動(dòng)端翻頁(yè)插件dropload.js(支持Zepto和jQuery)
dropload.js提供了最基本的上拉翻頁(yè),下拉刷新功能。對(duì)于由服務(wù)端一次返回所有數(shù)據(jù)的情況基本通用。
但是,需求往往不是服務(wù)端一次性返回所有數(shù)據(jù),往往還要支持服務(wù)端分頁(yè),搜索,排序,多條件篩選等功能。(比較類似美團(tuán)美食的界面)
一、解決方案
改進(jìn)1:由于有分頁(yè),搜索,排序,多條件篩選功能,可能都不需要上拉,進(jìn)到頁(yè)面就沒有數(shù)據(jù)。
例如:搜索一個(gè)服務(wù)端不存在的名字。
所以,添加接口設(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); } };
改進(jìn)2:由以上問題還引發(fā)了一個(gè)bug,選擇不同的篩選條件,然后上拉加載更多,此時(shí)沒有反應(yīng)了。
原因較復(fù)雜,舉例說明:選擇不同的篩選條件,數(shù)據(jù)量不一樣,如果不執(zhí)行resetload,那么頁(yè)面的的上拉計(jì)算距離就存在問題。
1. 只要發(fā)API到服務(wù)端,無論返回成功失敗,都必須執(zhí)行resetload,成功時(shí)需要在加載完全部新增的數(shù)據(jù)后resetload。
2. 更改resetload如下,添加調(diào)用計(jì)算屏幕尺寸的方法。
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. 解決以上兩個(gè)問題,基本解決了90%的問題,還有一個(gè)是setHasData(false)之后的處理。(假設(shè)每頁(yè)的count時(shí)20條)
bug: 在篩序條件1:返回20條數(shù)據(jù),上拉加載更多返回10條數(shù)據(jù),此時(shí)設(shè)置setHasData(false)。選擇篩選條件2,返回20條數(shù)據(jù),上拉加載,你會(huì)驚奇的發(fā)現(xiàn)拉不動(dòng)了。
why: setHasData(false)之后狀態(tài)還停留在沒有更多數(shù)據(jù)的狀態(tài)。此時(shí)應(yīng)該鎖定了上拉加載,更改篩選條件后,沒有解除鎖定,所以不能上拉加載了。
解決方法:每次更改搜索條件,篩選條件,排序等時(shí),都需要設(shè)置setHasData(true)。
二、調(diào)用方法
整體頁(yè)面邏輯較復(fù)雜。這里在整體解釋一遍。
1. 選擇要上拉加載的DIV,添加調(diào)用方法。
注意事項(xiàng):
(1)記得保存返回對(duì)象。
(2)LoadDownFn時(shí)上拉加載后的回調(diào),這里自己要處理的邏輯。我這里時(shí)翻頁(yè)發(fā)API,API參數(shù)中offset加20,然后發(fā)API。
(3)無論API返回失敗成功,都必須resetload。
這里強(qiáng)調(diào):
fetchData函數(shù)調(diào)用發(fā)API,失敗或者成功都必須self.moreFund.resetload()。
并且失敗時(shí)直接調(diào)用self.moreFund.resetload()即可。成功時(shí)要在新的數(shù)據(jù)返回后,要先用JS動(dòng)態(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)什么時(shí)候需要設(shè)置true。
當(dāng)非翻頁(yè)API觸發(fā)之前。即選擇新的篩選條件,選擇新的搜索字段,選擇新的排序字段。這個(gè)時(shí)候必須setHasData(true)。
this.moreFund.setHasData(true);
(2)什么時(shí)候設(shè)置false。
服務(wù)端返回?cái)?shù)據(jù)后,比較服務(wù)端返回的條目數(shù)與API發(fā)送的條目數(shù)是否一致,不一致設(shè)置setHasData(false)。
if (data.length < this.apiParams.count){ this.moreFund.setHasData(false); this.moreFund.lock(); }
3. lock與unlock詳解
(1)什么時(shí)候設(shè)置lock。
服務(wù)端返回?cái)?shù)據(jù)后,比較服務(wù)端返回的條目數(shù)與API發(fā)送的條目數(shù)是否一致,不一致設(shè)置lock()。
當(dāng)前頁(yè)面狀態(tài)不需要上拉加載時(shí)需要設(shè)置lock()。例如:在搜索框輸入的狀態(tài)。
(2)什么時(shí)候設(shè)置unlock。
只有一個(gè)地方需要調(diào)用。發(fā)送API之前設(shè)置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ù)據(jù)</span></div>' }, distance: 50, // 拉動(dòng)距離 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; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- jsp實(shí)現(xiàn)上一頁(yè)下一頁(yè)翻頁(yè)功能(示例代碼)
- JavaScript中transform實(shí)現(xiàn)數(shù)字翻頁(yè)效果
- JavaScript實(shí)現(xiàn)翻頁(yè)功能(附效果圖)
- 基于Vuejs框架實(shí)現(xiàn)翻頁(yè)組件
- Js實(shí)現(xiàn)網(wǎng)頁(yè)鍵盤控制翻頁(yè)的方法
- js實(shí)現(xiàn)翻頁(yè)后保持checkbox選中狀態(tài)的實(shí)現(xiàn)方法
- 一個(gè)JS翻頁(yè)效果
- javascript移動(dòng)端 電子書 翻頁(yè)效果實(shí)現(xiàn)代碼
相關(guān)文章
JavaScript代碼簡(jiǎn)化技巧實(shí)例解析
這篇文章主要介紹了JavaScript代碼簡(jiǎn)化技巧實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09underscore之Chaining_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文通過文字說明與代碼的形式給大家介紹了underscore之Chaining的相關(guān)知識(shí),感興趣的朋友一起學(xué)習(xí)吧2017-07-07JS Excel讀取和寫入操作(模板操作)實(shí)現(xiàn)代碼
前一段時(shí)間一直在做報(bào)表,所以肯定會(huì)用到Excel的操作,但是在網(wǎng)上查閱資料有關(guān)JS操作excel較少,有的話,也都是老生常談或很零碎的一些東西。2010-04-04鼠標(biāo)移到div,浮層顯示明細(xì),彈出層與div的上邊距左邊距重合(示例代碼)
這篇文章主要介紹了鼠標(biāo)移到div,浮層顯示明細(xì),彈出層與div的上邊距左邊距重合的實(shí)例代碼。需要的朋友可以過來參考下,希望對(duì)大家有所幫助2013-12-12wap手機(jī)端解決返回上一頁(yè)的js實(shí)例
下面小編就為大家?guī)硪黄獁ap手機(jī)端解決返回上一頁(yè)的js實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12JavaScript使用原型和原型鏈實(shí)現(xiàn)對(duì)象繼承的方法詳解
這篇文章主要介紹了JavaScript使用原型和原型鏈實(shí)現(xiàn)對(duì)象繼承的方法,簡(jiǎn)單講述了javascript原型與原型鏈的原理,并結(jié)合實(shí)例形式詳細(xì)分析了javascript中對(duì)象繼承的常見實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-04-04