这是手机端常见的一个功能,可能很多人都是用框架或者插件实现。
这里,我试着用原生js实现。
这样能更明白原理与底层实现
首先,布局,模拟初始数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Document</title> <style> body { margin: 0; } html, body { height: 100%; } header, footer { width: 100%; height: 40px; position: absolute; left: 0; text-align: center; line-height: 40px; background: #999999; color: #ffffff; z-index: 999; } header { top: 0; } footer { bottom: 0; } ul { display: block; width: 100%; position: absolute; top: 40px; bottom: 40px; overflow: auto; list-style: none; padding: 0; margin: 0; } ul>li { width: 100%; height: 40px; line-height: 40px; text-indent: 20px; border-bottom: 1px solid #666666; background: #ffffff; color: #333333; } /* 下拉刷新的时候做 */ #loading{ width: 100%; height: 40px; line-height: 40px; text-align: center; color: #333333; transition: all 0.5s; position: absolute; z-index: 1; color: #ffffff; background: orange; top: 0; } </style> </head> <body> <header> 我是头部 </header> <section id="con"> <div id="loading">加载中......</div> <ul id="list"> </ul> </section> <div id="loadEnd">已加载全部数据</div> <footer> 我是尾部 </footer> </body> <script> <script> //获取数据 var list = document.getElementById("list"); function getData() { var html = ''; for (var i = 0; i < 20; i++) { html += '<li>我是第' + (i + 1) + '个li</li>'; } list.innerHTML = html; } //初始加载函数 window.onload = () => { //初始请求数据 getData(); } </script> </script> </html>
会得到这样一个网页,头部底部固定,中间部分可滑动
接下来,我们来监听ul的滚动事件
list.addEventListener("scroll", function () { //这里可以获取到ul距离屏幕顶部的距离,每次滚动都会刷新 console.log(this.scrollTop); })
来做一个分析,接下来不要着急写代码
看到这个图,我们就知道要做什么了
//ul的高度 不变的 定死的 let listH = list.clientHeight; //所有li总高度 let contentH = this.childNodes.length * 41; //差值 let diffValue = contentH - listH; //距离视窗还有50的时候,开始触发; if (this.scrollTop + 50 >= diffValue) { console.log('该加载了...') getData(); }
上滑加载完美实现,当我滑到快到最后一个li的时候,触发获取数据的方法
我们再添加一个节流阀,不让它无限加载。
function getData() { var html = ''; for (var i = 0; i < 20; i++) { html += '<li>我是第' + (i + 1) + '个li</li>'; } var length = list.children.length; if (length === 0) { list.innerHTML = html; } else if(length > 0 && length < 100){ //html是字符串 var newHtml = parseDom(html); //后面插入元素 insertAfter(newHtml, list.children[length - 1]); }else if(length === 100){ console.log("已经到底了,别拉了"); } }
这里有两个非常重要的方法,都是原生js操作
1.字符串dom化
//字符串dom化 function parseDom(arg) { var objEle = document.createElement("div"); objEle.innerHTML = arg; return [...objEle.childNodes]; };
2.在已有元素后面插入元素
//在已有元素后面插入元素 function insertAfter(newElement, targetElement) { newElement.forEach(element => { //在后面插入元素 after:js新的dom api targetElement.after(element) }); return }
下拉加载
通过判断ul的scrollTop值,当ul的scrollTop === 0的时候,触发
添加到监听滚动事件里,加一些样式操作即可
有些许粗糙,功能为主,见谅见谅
if(this.scrollTop === 0){ list.style.top = "80px"; loading.style.top = "40px"; //刷新数据 setTimeout(()=>{ loading.style.top = "0"; list.style.top = "40px"; },1000) }
那么完整代码就是
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Document</title> <style> body { margin: 0; } html, body { height: 100%; } header, footer { width: 100%; height: 40px; position: absolute; left: 0; text-align: center; line-height: 40px; background: #999999; color: #ffffff; z-index: 999; } header { top: 0; } footer { bottom: 0; } ul { display: block; width: 100%; position: absolute; top: 40px; bottom: 40px; overflow: auto; list-style: none; padding: 0; margin: 0; } ul>li { width: 100%; height: 40px; line-height: 40px; text-indent: 20px; border-bottom: 1px solid #666666; background: #ffffff; color: #333333; } /* 下拉刷新的时候做 */ #loading,#loadEnd{ width: 100%; height: 40px; line-height: 40px; text-align: center; color: #333333; transition: all 0.5s; position: absolute; z-index: 1; color: #ffffff; } #loading{ background: orange; top: 0; } #loadEnd{ background: green; bottom: 0; } </style> </head> <body> <header> 我是头部 </header> <section id="con"> <div id="loading">加载中......</div> <ul id="list"> </ul> </section> <div id="loadEnd">已加载全部数据</div> <footer> 我是尾部 </footer> <script> //获取数据 var list = document.getElementById("list"); var loading = document.getElementById("loading"); var loadEnd = document.getElementById("loadEnd"); function getData() { var html = ''; for (var i = 0; i < 20; i++) { html += '<li>我是第' + (i + 1) + '个li</li>'; } var length = list.children.length; if (length === 0) { list.innerHTML = html; } else if(length > 0 && length < 100){ //html是字符串 var newHtml = parseDom(html); //后面插入元素 insertAfter(newHtml, list.children[length - 1]); }else if(length === 100){ console.log("已经到底了,别拉了"); list.style.bottom = "80px"; loadEnd.style.bottom = "40px"; //加个定时器模拟接口请求结束 隐藏掉此条 //或者可以插入一条元素 } } //字符串dom化 function parseDom(arg) { var objEle = document.createElement("div"); objEle.innerHTML = arg; return [...objEle.childNodes]; }; //在已有元素后面插入元素 function insertAfter(newElement, targetElement) { newElement.forEach(element => { //在后面插入元素 js 新的dom api targetElement.after(element) }); return } //初始加载函数 window.onload = () => { //初始请求数据 getData(); list.addEventListener("scroll", function () { //ul的高度 不变的 定死的 let listH = list.clientHeight; //所有li总高度 let contentH = this.childNodes.length * 41; //下拉刷新 if(this.scrollTop === 0){ list.style.top = "80px"; loading.style.top = "40px"; //刷新数据 setTimeout(()=>{ loading.style.top = "0"; list.style.top = "40px"; },1000) } //距离 let diffValue = contentH - listH; //ul离顶部的距离 //距离视窗还有50的时候,开始触发; if (this.scrollTop + 50 >= diffValue) { console.log('该加载了...') getData(); } }) } </script> </body> </html>