先看效果
不加防抖节流。
加防抖节流
提示
加防抖节流 可以节省性能,但滑动过快会出现闪烁现象。请根据需求自行调整触发时间。原理通过代码可看出!
限制:
该代码处理方式由于html 元素高度存在最大值,且不同浏览器以及不同浏览器版本支持的html 元素高度最大值不一样,当数据列表理论生成的高度大于html元素高度最大值时,生成的滚动区域只有html元素高度最大值那部分,剩下部分无法显示出来。
代码
不加防抖节流
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</title>
<meta name="viewport" content="initial-scale=1.0">
<meta name="format-detection" content="telephone=no, email=no">
</head>
<body>
<header>
<!-- <h1>无限滚动中的虚拟列表-防抖和节流(只渲染可视区域,dom元素可复用)</h1> -->
<h1>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</h1>
</header>
</article>
<article class="d-part d-effect">
<style>
.container {
height: 600px;
overflow: auto;
}
.item {
min-height: 60px;
border-bottom: 1px solid #cccccc;
border-top: 1px solid #cccccc;
width: 100%;
text-align: center;
background-color: darkgray;
/* padding: 30px 0;
box-sizing: border-box; */
}
</style>
<div class="container">
<div class="content">
<div class="viewArea">
<div class="item">0</div>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
</div>
</div>
<script>
var item = document.querySelector('.viewArea .item'); //需要渲染的单个列表元素
var container = document.querySelector('.container'); //可视区域元素
console.log(item);
var start = 0; // 开始位置
var pageSize = 10; // 每页展示的数据
var total = 100000; //数据总长度
// var itemHeight = 61; // 每个item的高度
var itemStyle = getComputedStyle(item); // 获取元素最终样式
var itemHeight = Number(itemStyle.height.split('px')[0]) + Number(itemStyle.borderTopWidth.split('px')[0]) + Number(itemStyle.borderBottomWidth.split('px')[0]); // 每个item的高度
console.log('itemHeight', itemHeight);
// 设置数据列表的总高度
document.querySelector('.container .content').style.height = itemHeight * total + 'px';
updateDom(start, pageSize, itemHeight, 0);
//更新渲染列表函数
function updateDom(start, pageSize, itemHeight, height) {
document.querySelector('.container .content .viewArea').style.transform = 'translateY(' + height + 'px)';
let all = document.querySelectorAll(' .viewArea .item'); // 获取所有渲染列表
for (var i = start, itemIndex = 0, len = start + pageSize; i < len; i++, itemIndex++) {
var index = i % pageSize; // 计算出数据列表的下标
all[itemIndex].innerHTML = i;
}
}
// 滚动处理函数
function handleScroller() {
var lastStart = 0; // 上次开始的位置
return () => {
var currentScrollTop = container.scrollTop;
var fixedScrollTop = currentScrollTop - currentScrollTop % itemHeight;
var start = Math.floor(currentScrollTop / itemHeight);
if (lastStart !== start) {
lastStart = start;
updateDom(start, pageSize, itemHeight, fixedScrollTop);
}
}
}
// // 防抖和节流
// function throttle(fn, delay, atleast) {
// let timer = null;
// let rAFtimer = null;
// let previous = 0;
// return function () {
// let now = Date.now();
// if (now - previous > atleast) {
// console.log('now - previous > atleast');
// fn();
// previous = now;
// } else {
// if (delay > 20) {
// console.log('delay > 20');
// clearTimeout(timer);
// timer = setTimeout(function () {
// fn();
// previous = 0;
// }, delay);
// } else {
// console.log('delay < 20');
// cancelAnimationFrame(rAFtimer);
// rAFtimer = requestAnimationFrame(function () {
// requestIdleCallback(fn);
// });
// }
// }
// }
// }
// document.querySelector('.container').addEventListener('scroll', throttle(handleScroller(), 16, 500), false);
document.querySelector('.container').addEventListener('scroll', handleScroller(), false);
</script>
</article>
</div>
</body>
</html>
加防抖节流
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</title>
<meta name="viewport" content="initial-scale=1.0">
<meta name="format-detection" content="telephone=no, email=no">
</head>
<body>
<header>
<h1>无限滚动中的虚拟列表-防抖和节流(只渲染可视区域,dom元素可复用)</h1>
<!-- <h1>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</h1> -->
</header>
</article>
<article class="d-part d-effect">
<style>
.container {
height: 600px;
overflow: auto;
}
.item {
min-height: 60px;
border-bottom: 1px solid #cccccc;
border-top: 1px solid #cccccc;
width: 100%;
text-align: center;
background-color: darkgray;
/* padding: 30px 0;
box-sizing: border-box; */
}
</style>
<div class="container">
<div class="content">
<div class="viewArea">
<div class="item">0</div>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
</div>
</div>
<script>
var item = document.querySelector('.viewArea .item');
var container = document.querySelector('.container');
console.log(item);
var start = 0; // 开始位置
var pageSize = 10; // 每页展示的数据
var total = 100000; //数据总长度
// var itemHeight = 61; // 每个item的高度
var itemStyle = getComputedStyle(item);
var itemHeight = Number(itemStyle.height.split('px')[0]) + Number(itemStyle.borderTopWidth.split('px')[0]) + Number(itemStyle.borderBottomWidth.split('px')[0]); // 每个item的高度
console.log('itemHeight', itemHeight);
// 设置数据列表的总高度
document.querySelector('.container .content').style.height = itemHeight * total + 'px';
updateDom(start, pageSize, itemHeight, 0);
function updateDom(start, pageSize, itemHeight, height) {
document.querySelector('.container .content .viewArea').style.transform = 'translateY(' + height + 'px)';
let all = document.querySelectorAll(' .viewArea .item');
for (var i = start, itemIndex = 0, len = start + pageSize; i < len; i++, itemIndex++) {
var index = i % pageSize;
all[itemIndex].innerHTML = i;
}
}
// 滚动处理函数
function handleScroller() {
var lastStart = 0; // 上次开始的位置
return () => {
var currentScrollTop = container.scrollTop;
var fixedScrollTop = currentScrollTop - currentScrollTop % itemHeight;
var start = Math.floor(currentScrollTop / itemHeight);
if (lastStart !== start) {
lastStart = start;
updateDom(start, pageSize, itemHeight, fixedScrollTop);
}
}
}
// 防抖和节流
function throttle(fn, delay, atleast) {
let timer = null;
let rAFtimer = null;
let previous = 0;
return function () {
let now = Date.now();
if (now - previous > atleast) {
console.log('now - previous > atleast');
fn();
previous = now;
} else {
if (delay > 20) {
console.log('delay > 20');
clearTimeout(timer);
timer = setTimeout(function () {
fn();
previous = 0;
}, delay);
} else {
console.log('delay < 20');
cancelAnimationFrame(rAFtimer);
rAFtimer = requestAnimationFrame(function () {
requestIdleCallback(fn);
});
}
}
}
}
document.querySelector('.container').addEventListener('scroll', throttle(handleScroller(), 16, 500), false);
// document.querySelector('.container').addEventListener('scroll', handleScroller(), false);
</script>
</article>
</div>
</body>
</html>