1.触底无限加载
原理:监听父元素的滚动事件;利用元素的滚动距离scrollTop与元素的视口的高度clientHeight之和与该元素的滚动高度scrollHeight进行比较,即el.scrollTop + el.clientHeight >= el.scrollHeight 时,说明元素滚动到底部了,此时,可以调用接口,再加载数据
下面是一个js模拟触底无限加载的效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
* {
padding: 0;
margin: 0;
}
.demo {
width: 500px;
height: 800px;
border: 1px solid #f5f;
margin: auto;
overflow-y: auto;
}
.demo::-webkit-scrollbar {
display: none;
}
.item {
width: calc(100% - 20px);
margin: 10px;
}
.load-more {
width: 60px;
height: 60px;
text-align: center;
margin: 0 auto;
line-height: 60px;
border-top: 1px solid #ddd;
transition: transform 0.3s ease-in-out;
}
.text {
transition: opacity 0.3s ease-in-out;
}
.loading {
animation: scroll 1s infinite linear;
}
@keyframes scroll {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<body>
<div class="demo">
<div class="list"></div>
<div class="load-more">
<div class="text">加载更多</div>
</div>
</div>
<script>
var i = 0;
var data = Array.from({ length: 50 }, () => ({ name: ++i, age: parseInt(Math.random() * 100) })) //随机创建数据
var moreData = Array.from({ length: 20 }, () => ({ name: "new" + ++i, age: parseInt(Math.random() * 100) })) //更多的数据
var demo = document.querySelector(".demo") //获取最外层容器
var list = document.querySelector(".list") //获取列表容器
var loadMore = document.querySelector(".load-more") //获取加载更多容器
var text = document.querySelector(".text") //获取父容器
var isLoading = false //是否在加载
data.forEach(item => {
let itemEl = document.createElement("div");
itemEl.classList.add("item");
itemEl.innerText = `name: ${item.name}; age: ${item.age}`
list.appendChild(itemEl)
})
demo.addEventListener("scroll", handleScroll) //监听滚动事件
function handleScroll() {
if (demo.scrollTop + demo.clientHeight >= demo.scrollHeight && !isLoading) {
isLoading = true;
text.style.opacity = "0"
loadMore.style.transform = "translateY(0)"
loadMore.classList.add("loading")
setTimeout(() => {
addDataChild()
}, 2000)
}
}
function addDataChild() { //添加数据
moreData.forEach(item => {
let itemEl = document.createElement("div");
itemEl.classList.add("item");
itemEl.innerText = `name: ${item.name}; age: ${item.age}`
list.appendChild(itemEl)
})
resetStyle()
}
function resetStyle() { //重置样式
text.style.opacity = "1"
loadMore.style.transform = "translateY(100%)"
loadMore.classList.remove("loading")
isLoading = false
}
</script>
</body>
</html>
2.上滑刷新
原理:利用dom操作,监听滑动事件
demo.addEventListener("touchstart", handleStart) //开始滑动
demo.addEventListener("touchmove", handleMove) //滑动中
demo.addEventListener("touchend", handleEnd) //滑动结束
//touchstart和touchend都只会触发一次
//touchmove在滑动过程中会一直触发
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
.demo {
width: 500px;
height: 700px;
margin: 100px auto;
border: 1px solid #ddd;
background: #f5f5f5;
overflow-y: auto;
}
.fresh {
transition: transform 0.3 ease-in-out;
height: 20px;
}
.arrow {
position: relative;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3 ease-in-out;
}
.arrow::before {
content: "";
position: absolute;
top: 2px;
width: 6px;
height: 6px;
border: 1px solid transparent;
border-top: 1px solid red;
border-right: 1px solid red;
transform: rotate(-45deg);
}
.arrow::after {
content: "";
position: absolute;
top: 2px;
width: 2px;
height: 16px;
background: red;
}
.loading {
animation: scroll 1s infinite linear;
}
@keyframes scroll {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.item {
height: 20px;
line-height: 20px;
margin: 10px;
}
</style>
<body>
<div class="demo">
<div class="fresh">
<div class="arrow"></div>
</div>
<div class="list">
</div>
</div>
<script>
let demo = document.querySelector(".demo")
let fresh = document.querySelector(".fresh")
let arrow = document.querySelector(".arrow")
let list = document.querySelector(".list")
let initData = Array.from({ length: 50 }, () => Math.random() * 50) //初始化数据
let startY = null, currentY = null, isRefresh = false;
initData.forEach(el => {
let elItem = document.createElement("div")
elItem.classList.add("item")
elItem.innerText = el
list.appendChild(elItem)
})
demo.addEventListener("touchstart", handleStart) //开始滑动
demo.addEventListener("touchmove", handleMove) //滑动中
demo.addEventListener("touchend", handleEnd) //滑动结束
function handleStart(e) {
if (demo.scrollTop === 0) {
startY = e.touches[0].clientY
}
}
function handleMove(e) {
if (startY === null) { //若没有到页面顶部,则不让计算滑动距离
return
}
console.log("e", e);
currentY = e.touches[0].clientY;
const diffY = currentY - startY //计算距离
if (diffY > 0 && !isRefresh) {
e.preventDefault()
fresh.style.transform = `translateY(${diffY}px)`;
if (diffY >= 60) { //滑动到一定距离才开始计算刷新
arrow.style.transform = `rotate(180deg)`
} else {
arrow.style.transform = `rotate(0deg)`
}
}
}
function handleEnd() {
if (startY === null) {
return
}
if (currentY - startY >= 60 && !isRefresh) {
isRefresh = true; //开始处理加载
arrow.style.transform = `rotate(0deg)`
fresh.style.transform = `translateY(0)`;
startY = null
currentY = null
addDaat() //加载新数据
} else {
startY = null
currentY = null
isRefresh = false
arrow.style.transform = `rotate(0deg)`
fresh.style.transform = `translateY(0)`;
}
}
function addDaat() {
initData.forEach(el => {
let elItem = document.createElement("div")
elItem.classList.add("item")
elItem.innerText = el
list.appendChild(elItem)
})
isRefresh = false //重置下拉状态
}
</script>
</body>
</html>