虚拟列表用于前端列表性能优化,通过对数据的截取和展示区域的定位,达到只渲染展示区域+1数量的列表项,以下为代码
<!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>virtual-list</title>
<style>
.context {
position: relative;
width: 500px;
max-height: 600px;
background-color: #cccccc;
overflow: auto;
}
.virtual-context {
position: absolute;
left: 0;
right: 0;
z-index: -1;
}
.real-context {
position: absolute;
left: 0;
right: 0;
transform: translate3d(0);
}
.real-item {
width: 100%;
height: 150px;
background-color: #f2f2f2;
text-align: center;
line-height: 150px;
}
</style>
</head>
<body>
<div id="context" class="context">
<!-- 用于撑开context -->
<div id="virtualContext" class="virtual-context"></div>
<!-- 用于渲染列表项 -->
<div id="realContext" class="real-context">
</div>
</div>
<script>
const data = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
]
const context = document.getElementById('context')
const virtualContext = document.getElementById('virtualContext')
const realContext = document.getElementById('realContext')
// 列表每项高度
const itemHeight = 150
// 列表父容器的最大高度
const maxHeight = 600
// 列表最多展示数量
const itemNum = Math.ceil(maxHeight/itemHeight) + 1
// 根据数据量设置列表父容器高度
context.style.height = data.length * itemHeight >= maxHeight ? maxHeight + 'px' : data.length * itemHeight + 'px'
// 根据数据量设置父容器的滚动高度,控制是否展示滚动条
virtualContext.style.height = data.length * itemHeight + 'px'
// 数据截取起始
let start = 0;
// 数据截取终点
let end = data.length > (start + itemNum) ? (start + itemNum) : data.length
// 渲染列表项
const renderList = () => {
let str = ''
for (let i = start; i < end; i++) {
str += `<div class="real-item">${data[i]}</div>`
}
realContext.innerHTML = str
}
renderList()
context.onscroll = (e) => {
const scrollTop = e.target.scrollTop
const scrollHeight = e.target.scrollHeight
const clientHeight = e.target.clientHeight
// 如果滚动条触底则不继续执行
if (scrollHeight - scrollTop !== clientHeight) {
// 根据滚动距离和列表项高度,获取新起点
const newStart = Math.floor(scrollTop / itemHeight)
// 优化:如果起点不变,不执行渲染
if (newStart !== start) {
start = newStart
end = data.length > (start + itemNum) ? (start + itemNum) : data.length
// 重点:设置列表距离顶部高度
const offsetTop = scrollTop - (scrollTop % itemHeight)
realContext.style.transform = `translate3d(0, ${offsetTop}px, 0)`
renderList()
}
}
}
</script>
</body>
</html>