今天分享一下原生如何实现一个简单虚拟列表。
什么是虚拟列表
虚拟列表(Virtual List)是一种优化大型列表渲染性能的技术。在传统的列表渲染中,如果列表中有大量数据,那么所有的数据都会被一次性渲染出来,这会导致内存占用和渲染时间的大幅增加。
虚拟列表的原理:
虚拟列表的原理是只渲染当前可见区域内的数据,而不是一次性渲染整个列表。当用户向下滚动时,虚拟列表会动态地渲染新的数据并同时移除已经离开可见区域的数据。这种技术可以大大减少内存占用和渲染时间,提高列表的性能和用户体验。这里引用`yuan溜溜`作者的原理图,如下所示:
代码的实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.container {
width: 400px;
height: 500px;
border: 1px black solid;
overflow: auto;
}
</style>
<body>
<div class="container">
<ul class="parent">
</ul>
</div>
<script>
const list = []
// 准备数据
for (let index = 0; index < 100; index++) {
list.push(index)
}
const containers = document.querySelector('.container')
const parent = document.querySelector('.parent') // 获取ul
const children = `Lorem ipsum dolor sit amet consectetur adipisicing elit.` // 文本
let itemheight = 41.6 //规定子项的高度
let containerHeight = containers.getBoundingClientRect().height // 获取container的高度
const visibleItemCount = Math.ceil(containerHeight / itemheight); // 可见区域内最多能显示多少个列表项
let startIndex = 0 //设置默认开始的数据位置
let endIndex = visibleItemCount //设置默认结束的数据位置 = 可见区域内最多能显示多少个列表项
// 渲染函数
function renderList(startIndex, endIndex) {
const frag = document.createDocumentFragment()
for (let index = startIndex; index < endIndex; index++) {
// 当大于或等于list的长度是跳出循环,注意不要return跳出函数不然快速滑动滚动条的时候会有bug
if (index >= list.length) {
break
}
const li = document.createElement('li')
li.innerText = children + list[index]
frag.appendChild(li)
}
// 清空ul里面的节点,注意不要写在函数最前面。
parent.innerHTML = ''
// 在渲染节点 保证只有visibleItemCount个节点
parent.appendChild(frag)
// 设置padding撑起来滚动条
parent.setAttribute("style", `padding-top: ${startIndex * itemheight}px;padding-bottom:${(list.length - endIndex) * itemheight}px`);
}
// 第一次渲染可见的数据
renderList(startIndex, endIndex)
// 事件的回调函数
const handleScroll = () => {
const scrollTop = containers.scrollTop // 获取滑动了多少px
startIndex = Math.floor(scrollTop / itemheight) // 滑动了多少减去 / 每个子项的高度 = scrollTop里面有多少条数据 = 开始的数据位置
endIndex = startIndex + visibleItemCount // 开始的数据位置 + 可视区域的可显示的条数 = 结束的数据位置
renderList(startIndex, endIndex) //渲染函数
}
containers.addEventListener('scroll', handleScroll) //绑定滚动事件
</script>
</body>
</html>