基于mui的标签页的上拉加载问题再升级-页面嵌套双滚动+标签页动态加载+上拉加载
理想很丰满。在实际开发过程中,总是会遇到一些看似很简单很简单但总也是绕不过去的问题。先来说一下这次要说的问题吧(是基于mui的移动端开发):标签页外部(也就是标签页上面)有占位区和搜索框,标签页内部需要上拉加载(或下来刷新)。有人会说了加一个overflow: auto; 或 hoverflow: scroll; 不就行了,要是可以的话你就不会看到这篇博文了。当然你也可以尝试添加 mui-scroll-wrapper 和 mui-scroll 然后通过 mui('.mui-scroll-wrapper').scroll(); 初始化滚动,当时你会发现 “欸~ 页面怎么动不了了” “欸~ 怎么动了一下就不动了”。 其实这些我都已进行尝试,并在在个问题上整整磕了两天终于弄明白些了:mui是通过监听按压推拽来进行页面滚动的,然后我在通过监听滚动获取滚动位置来及时清除和添加相应监听, 欸~ 问题解决了。 好了话不多说先看效果图:
以下是代码(代码量比较大但是比较详细,喜欢的话还请耐心看完):
html:
<!doctype html> <html> <head> <meta charset="utf-8"> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link type="text/css" rel="stylesheet" href="../css/mui.min.css" /> <link type="text/css" rel="stylesheet" href="../css/advanced.search.css" /> <style type="text/css"> .test { width: 100%; color: #FF0000; font-size: 16px; line-height: 30px; text-align: center; } .brf { top: 0; left: 0; color: red; z-index: 999; position: fixed; max-width: 100%; font-size: 13px; padding: 6px 10px; background: rgba(100, 100, 100, .2); } </style> </head> <body> <header class="mui-bar mui-bar-nav" style="background: #ffffff; box-shadow: none;"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">测试scroll</h1> </header> <nav class="mui-bar mui-bar-tab no-border"> <button id="collectBtn" type="button" class="mui-btn mui-btn-primary mui-btn-outlined"> <span class="mui-icon mui-icon-star"></span> 要收藏吗 </button> </nav> <!-- 搜索框 --> <div class="mui-input-row advanced-search content-search-box"> <span class="mui-icon mui-icon-search"></span> <input id="searchInput" type="text" class="mui-input-clear" placeholder="请输入您要查询的关键字"> <a href="#picture" class="mui-icon mui-icon-camera" style="display: inline-block;"></a> <span class="mui-icon mui-icon-mic" onclick="startaptureAudio()"></span> </div> <div class="mui-content mui-scroll-wrapper outer"> <div class="mui-scroll"> <div id="outerTopBox" class="outer-box test" style="border: solid 1px #0000ff;"> <div>3123123132</div> <div>3123123132</div> <div>3123123132</div> <div>3123123132</div> <div>3123123132</div> <div>3123123132</div> <div>3123123132</div> </div> <div id="slider" class="mui-slider"> <div id="sliderSegmentedControl" class=""> <div id="searchSlider" class="mui-scroll"></div> </div> <div id="searchSliderGroup" class="mui-slider-group"></div> </div> </div> </div> <div class="brf"> <div id="outerY"></div> <div id="innerY"></div> </div> </body> <script type="text/javascript" src="../js/mui.min.js"></script> <script type="text/javascript" src="../js/mui.pullToRefresh.js"></script> <script type="text/javascript" src="../js/mui.pullToRefresh.material.js"></script> <script type="text/javascript" src="../js/nest.scroll.tab.pullToRefresh.js"></script> <script type="text/javascript"> mui.init(); var nums = []; // 动态渲染标签页标签数据 setTimeout(function(){ var h1 = '', h2 = ''; for (var i = 0; i < 9; i++) { if (i == 0) { h1 += '<a id="tab' + i + '" class="mui-control-item mui-active" href="#c' + i + '">tab' + i + '</a>'; h2 += '<div id="c' + i + '" class="mui-slider-item mui-control-content mui-scroll-wrapper inner mui-active">\ <div id="cScroll' + i + '" class="mui-scroll" init="true" total="0" page-no="0" page-size="10">\ <div class="list-box">'; for (var j = 0; j < i + 3; j++) { h2 += '<div class="test test2">' + i + '-' + j + '</div>'; } h2 += '</div></div></div>'; } else { h1 += '<a id="tab' + i + '" class="mui-control-item" href="#c' + i + '">tab' + i + '</a>'; h2 += '<div id="c' + i + '" class="mui-slider-item mui-control-content mui-scroll-wrapper inner">\ <div id="cScroll' + i + '" class="mui-scroll" init="true" total="0" page-no="0" page-size="10">\ <div class="list-box">'; for (var j = 0; j < i + 3; j++) { h2 += '<div class="test test2">' + i + '-' + j + '</div>'; } h2 += '</div></div></div>'; } nums.push({num: 0}); } document.querySelector('#searchSlider').innerHTML = h1; document.querySelector('#searchSliderGroup').innerHTML = h2; // 注意:对于动态渲染的slider标签页务必要执行以下三个方法,否则标签页将无法正常使用,尤其是标签超过屏幕宽度时 /*1*/ document.querySelector('#sliderSegmentedControl').className = 'mui-scroll-wrapper mui-slider-indicator mui-segmented-control mui-segmented-control-inverted'; /*2*/ mui(".mui-scroll-wrapper.mui-slider-indicator").scroll(); /*3*/ var slider = mui('.mui-slider').slider(); // 初始化内外部滚动 和 上拉加载 initScrollAndPullToRefresh(); // 标签页监听 document.getElementById('slider').addEventListener('slide', function(e) { // activeSlide 当前活跃的标签页的索引值 activeSlide = e.detail.slideNumber; console.log('activeSlide -> ' + activeSlide); // 刷新当前活跃标签页的嵌套滚动和上拉加载的监听 refreshSlideScrollAndPullUp(); }); loadData(); }, 1000); // 上拉加载回调 function loadData() { console.log('loadData start -> '); // 获取当前活跃的标签页 中 mui-scroll 所在的dom元素 也是 上拉加载 所监听的元素所在 var activeScroll = getActiveSlideScroll(); // 模拟数据加载 可使用ajax等方法替换 setTimeout(function() { console.log('loadData start render -> '); for (var i = 0; i < 10; i++) { var div = document.createElement('div'); div.innerHTML = '第 ' + activeSlide + ' 标签页,第 ' + nums[activeSlide].num + '-' + i + '个div!!!'; activeScroll.querySelector('.list-box').appendChild(div); } nums[activeSlide].num++; console.log('nums[' + activeSlide + '].num -> ' + nums[activeSlide].num); // 继续 或 结束上拉加载 第二个参数 false:可以继续上拉加载 true:结束上拉加载 endPullUpToRefresh(activeScroll, nums[activeSlide].num > 2); }, 1500); } </script> </html>
advanced.search.css:
/*----------------advanced-search---------------*/ .mui-input-row.advanced-search { width: 85%; margin: 0 auto; padding: 26px 0 0; } .advanced-search .mui-icon { width: 30px; height: 30px; color: #999999; font-size: 24px; font-weight: bold; position: absolute; padding: 3px !important; margin: 7px 3px !important; } .advanced-search input { margin: 0; height: 45px; font-size: 12px; border-radius: 3px; padding: 0 70px 0 35px; border: solid 2px #00489D; } .mui-input-row.advanced-search .mui-input-clear~.mui-icon-clear { top: 33px; padding: 0; right: 65px; width: 18px; height: 18px; color: #ffffff; font-size: 19px; border-radius: 9px; background: #999999; font-weight: normal; } .mui-input-row.advanced-search .mui-input-clear~.mui-icon-clear:before { position: relative; content: '\e460'; left: -3.5px; top: -3.5px; } .advanced-search .mui-icon-camera { right: 40px; margin-right: 0 !important; } .advanced-search .mui-icon-mic { right: 5px; margin-left: 0 !important; } /*----------------advanced-search---------------*/ /*----------------advanced-search content-search---------------*/ .mui-input-row.advanced-search.content-search-box { width: 100%; padding: 0 10px; background: #ffffff; } .advanced-search.content-search-box .mui-icon { margin: 3px !important; } .advanced-search.content-search-box input { background: #f5f5f5; height: 36px; border: none; } .mui-input-row.advanced-search.content-search-box .mui-input-clear~.mui-icon-clear { top: 6px; } .advanced-search.content-search-box .mui-icon-camera { margin-right: 0 !important; } .advanced-search.content-search-box .mui-icon-mic { margin-left: 0 !important; } .mui-bar-nav~.mui-content .advanced-search.content-search-box~.mui-slider.mui-fullscreen { top: 80px; } /*----------------advanced-search content-search---------------*/ .mui-bar-nav~.content-search-box { position: fixed; z-index: 999; top: 44px; } .mui-bar-nav~.content-search-box~.mui-content { padding-top: 80px; } .mui-pull-bottom-tips { margin-bottom: 15px; text-align: center; color: #999999; } .mui-bar-nav~.content-search-box { position: fixed; z-index: 999; top: 44px; } .mui-bar-nav~.content-search-box~.mui-content { padding-top: 80px; } .mui-content .mui-scroll .mui-slider.mui-fullscreen { position: relative; top: 0; } .mui-pull-bottom-tips { margin-bottom: 15px; text-align: center; color: #999999; } .mui-bar-tab .mui-btn { float: right; line-height: 1; font-size: 15px; padding: 8px 13px; margin-right: 20px; border-radius: 20px; } .mui-bar-tab .mui-btn.mui-icon, .mui-bar-tab .mui-btn .mui-icon { font-size: 18px; } .mui-bar-tab .mui-btn.mui-icon::before, .mui-bar-tab .mui-btn .mui-icon::before { position: relative; top: -2px; }
nest.scroll.tab.pullToRefresh.js:
var num = 1, OTI = true, fullscreen, outerScrollObj, activeSlide = 0, innerScrollObjs, pullToRefreshObjs, contentOffsetTop = 80, sliderControllHeight = 38, scrollAndPullToRefreshInit = false, innerScroll = '.inner>.mui-scroll', fullDiv = document.createElement('div'), outerWrapper = '.mui-scroll-wrapper.outer', innerWrapper = '.mui-scroll-wrapper.inner', outerTopHeight = document.querySelector("#outerTopBox") ? document.querySelector("#outerTopBox").clientHeight : 0, navBarTabHeight = document.querySelector('nav.mui-bar-tab') ? document.querySelector('nav.mui-bar-tab').clientHeight : 0, scrollOption = { scrollY: true, scrollX: false, bounce: true, // 开启回弹动画 indicators: false, //是否显示滚动条 deceleration: 0.0006 }; if (mui.os.ios) { scrollOption.deceleration = 0.003; } // 初始化页面的 滚动 和 上拉加载 function initScrollAndPullToRefresh() { fullDiv.setAttribute('style', 'position: fixed; bottom: 0; left: 0; top: 0;'); fullDiv.setAttribute('id', 'fullH'); document.body.appendChild(fullDiv); var hh = ''; fullscreen = fullDiv.clientHeight - contentOffsetTop; mui.each(document.querySelectorAll(innerWrapper), function(i, ele) { ele.style.height = (fullscreen - sliderControllHeight - navBarTabHeight) + 'px'; hh += ele.style.height + ','; }); console.log("fullscreen client height -> " + fullDiv.clientHeight); console.log("slider controll height -> " + sliderControllHeight); console.log("content offset top -> " + contentOffsetTop); console.log("nav bar tab height -> " + navBarTabHeight); console.log("outer top height -> " + outerTopHeight); console.log("fullscreen -> " + fullscreen); console.log("hh -> " + hh); if (document.querySelector('#brf')) { var d1 = document.createElement('div'); d1.innerHTML = 'fullscreen client height -> ' + fullDiv.clientHeight + '<br>' + "slider controll height -> " + sliderControllHeight + '<br>' + "content offset top -> " + contentOffsetTop + '<br>' + "nav bar tab height -> " + navBarTabHeight + '<br>' + "outer top height -> " + outerTopHeight + '<br>' + "fullscreen -> " + fullscreen + '<br>' + "hh -> " + hh; document.querySelector('#brf').appendChild(d1); } // 初始化内部上拉加载 pullToRefreshObjs = mui(innerScroll).pullToRefresh({ up: { height: 50, //可选.默认50.触发上拉加载拖动距离 auto: false, //可选,默认false.自动上拉加载一次 contentrefresh: "正在加载...", //可选,正在加载状态时,上拉加载控件上显示的标题内容 contentnomore: '没有更多数据了', //可选,请求完毕若没有更多数据时显示的提醒内容; callback: function() { //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据; loadData(); } } }); // 初始化内部滚动 innerScrollObjs = mui(innerWrapper).scroll(scrollOption); for (var i in innerScrollObjs) { innerScrollObjs[i].ITO = false; } // 初始化外部滚动 scrollOption.bounce = false; outerScrollObj = mui(outerWrapper).scroll(scrollOption); changeInnerEventListner('remove'); // 添加外部区域滚动事件监听 document.querySelector(outerWrapper).addEventListener('scroll', outerToInner, false); scrollAndPullToRefreshInit = true; } function outerToInner() { document.querySelector('#outerY')? document.querySelector('#outerY').innerHTML = 'outerY:' + outerScrollObj.y : ''; if (OTI && outerScrollObj.y <= (0 - outerTopHeight)) { // outerScroll.scrollTo(0, -200, 100); changeOuterEventListner('remove'); changeInnerEventListner('add'); changeIOScrollListner("OTI"); } } function innerToOuter() { document.querySelector('#innerY')? document.querySelector('#innerY').innerHTML = 'innerY' + activeSlide + ':' + innerScrollObjs[activeSlide].y : ''; if (innerScrollObjs[activeSlide].ITO && innerScrollObjs[activeSlide].y >= 0) { // innerScroll.scrollTo(0, 0, 100); changeInnerEventListner('remove'); changeOuterEventListner('add'); changeIOScrollListner("ITO"); } } // 添加 或 取消 内部滚动区域 滚动监听 和 上拉加载监听 function changeInnerEventListner(type) { if ('add' == type) { // 添加内部 滚动 和 推拽 的监听 document.querySelectorAll(innerWrapper)[activeSlide].addEventListener('drag', innerScrollObjs[activeSlide]); window.addEventListener('scroll', innerScrollObjs[activeSlide]); // 添加内部上拉加载有关的 上拉拖拽 和 滚动 监听 document.querySelectorAll(innerScroll)[activeSlide].addEventListener('dragup', pullToRefreshObjs[activeSlide]); window.addEventListener('scroll', pullToRefreshObjs[activeSlide]); } else if ('remove' == type) { // 取消内部 滚动 和 推拽 的监听 document.querySelectorAll(innerWrapper)[activeSlide].removeEventListener('drag', innerScrollObjs[activeSlide]); window.removeEventListener('scroll', innerScrollObjs[activeSlide]); // 取消内部上拉加载有关的 上拉拖拽 和 滚动 监听 document.querySelectorAll(innerScroll)[activeSlide].removeEventListener('dragup', pullToRefreshObjs[activeSlide]); window.removeEventListener('scroll', pullToRefreshObjs[activeSlide]); } } // 添加 或 取消 外部滚动区域 滚动监听 function changeOuterEventListner(type) { if ('add' == type) { document.querySelector(outerWrapper).addEventListener('drag', outerScrollObj); window.addEventListener('scroll', outerScrollObj); } else if ('remove' == type) { document.querySelector(outerWrapper).removeEventListener('drag', outerScrollObj); window.removeEventListener('scroll', outerScrollObj); } } // 切换内部外部滚动监听 function changeIOScrollListner(type) { if ('ITO' == type) { OTI = true; innerScrollObjs[activeSlide].ITO = false; setTimeout(function() { document.querySelectorAll(innerWrapper)[activeSlide].removeEventListener('scroll', innerToOuter, false); document.querySelector(outerWrapper).addEventListener('scroll', outerToInner, false); // innerScrollObjs[activeSlide].scrollListner = false; }, 1); } else if ('OTI' == type) { OTI = false; innerScrollObjs[activeSlide].ITO = true; setTimeout(function() { document.querySelector(outerWrapper).removeEventListener('scroll', outerToInner, false); document.querySelectorAll(innerWrapper)[activeSlide].addEventListener('scroll', innerToOuter, false); // innerScrollObjs[activeSlide].scrollListner = true; }, 1); } } // slider内容区 滚动 与 上拉 监听刷新 function refreshSlideScrollAndPullUp() { // 外部滚动到极限 内部未进行 滚动 和 上拉 监听 if (!OTI && !innerScrollObjs[activeSlide].ITO) { // 添加内部 滚动 和 上拉 监听 changeInnerEventListner('add'); innerScrollObjs[activeSlide].ITO = true; document.querySelectorAll(innerWrapper)[activeSlide].addEventListener('scroll', innerToOuter, false); } // 外部仍可滚动 内部存在 滚动 和 上拉 监听 if (OTI) { // 移除内部 滚动 和 上拉 监听 changeInnerEventListner('remove'); innerScrollObjs[activeSlide].ITO = false; document.querySelectorAll(innerWrapper)[activeSlide].removeEventListener('scroll', innerToOuter, false); } } // 获取当前显示的slider function getActiveSlideScroll() { return pullToRefreshObjs[activeSlide].element; } // 结束 或 继续上拉加载 function endPullUpToRefresh(activeScroll, flag) { mui(activeScroll).pullToRefresh().endPullUpToRefresh(flag); } // 重启上拉加载 function pullRefresh(activeScroll) { mui(activeScroll).pullToRefresh().refresh(true); } // 刷新外部滚动区上部高度 function refreshOuterTopHeight(ele) { if (ele) { outerTopHeight = document.querySelector(ele).clientHeight; } else { outerTopHeight = document.querySelector("#outerTopBox") ? document.querySelector("#outerTopBox").clientHeight : 0; } }
这里面需要注意的是: mui-scroll-wrapper outer inner 和 outerTopBox 这几个标识,缺一不可!!!
代码量比较大,可以拷贝下来直接在项目中运行,然后在慢慢研究!
这里只是一小部分,更多精彩功能还需各位小伙伴在开发中慢慢研究领悟参透!
关于上拉加载的一些其他问题比如分别在单页模式和标签页模式下停止上拉加载和重启上拉加载的区别可参考本人记录的另一篇博文 关于mui上拉加载遇到的一些问题
每天进步一点点,点滴记录,积少成多。
以此做个记录,
如有不足之处还望多多留言指教!