虚拟表格(第一种实现)
实现描述
**1.我们创建三个相互嵌套的标签,最外层的用于提供一个滚动的框架,中间层用于模拟列表的高度,里层用于存放渲染的数据
,最外层我们设置一下宽高并且使用overflow属性裁切一下,让他有滚动条,在最里层的子元素div标签设置一position:sticky 属性,这个属性
用于在目标区域在屏幕中的时候,他就相当于相对定位,当页面滚动超出目标区域时,就像固定定位,固定到目标位置
1.获取三个元素的dom节点
2.我们将最外层的盒子高度设置为滚动高度
3.其次计算初始的index
我们获取滚动条的高度除以每一项的高度即为初始的下标
4.写一个render函数,这个函数主要用于渲染
我们将计算出的开始节点以及要渲染的整个数组和渲染的长度传递进去;
然后我们将要渲染的数据获取出来,slice(起始,起始+渲染长度)
创建一个虚拟的节点对象
使用数组循环,创建dom节点并将其放入创建好的虚拟节点中
循环完成再将最里层的dom进行替换
再加一个监听器监听滚动事件每当滚动触发,就将重新计算初始的下标并重新调用render函数重新渲染
5. 初始化render函数(起始下标,渲染长度,数据源)
6.加一个滚动条监听事件用于重新计算初始下标,并且再次调用render函数
**
代码块
<!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>虚拟表格||大数据表格</title>
<style>
.virtual-scroll {
width: 200px;
height: 300px;
overflow-y: auto;
}
#container {
background-color: skyblue;
}
#container>div {
position: sticky;/* 这个属性能保证item-container能在渲染区域 哪怕滚动 */
top: 0;
}
</style>
</head>
<body>
<div class="virtual-scroll">
<!-- 提供滚动框架 -->
<div id="container">
<!-- // 模拟长列表高度 -->
<div id="item-container"></div>
<!-- // 存放渲染数据 -->
</div>
</div>
</body>
<script>
// 生成数据源,与逻辑无关
let list = [];
for (let i = 0; i < 10000; i++) {
let id = Math.random();
(
function () {
list.push({ id })
}
)(id)
}
// 正式开始
// 获取dom节点
const containerDom = document.querySelector("#container")
const scrollDom = document.querySelector(".virtual-scroll")
const itemDom = document.querySelector("#item-container")
// 设置滚动框架的高度(滚动高度)
containerDom.style.cssText = `height: ${ list.length * 30 }px;` // 模拟长列表
// 核心
const render = (initIndex = 0, data = []) => {
const renderData = data.slice(initIndex, initIndex + 10);
const fragment = document.createDocumentFragment();
renderData.forEach((item) => {
const div = document.createElement('div');
div.innerText = item.id
div.style.cssText = "height: 30px"
fragment.appendChild(div)
})
itemDom.replaceChildren(fragment)
}
// 初始化
render(0, list)
// 监听滚动条事件重新获取下标并重新渲染
scrollDom.addEventListener('scroll', scrollTarget => {
render( Math.ceil(scrollTarget.target.scrollTop / 30), list)
})
// })
</script>
</html>
渲染出的dom结构
第二种实现(表现渲染不卡顿,实际全部都渲染了)
实现描述
**1. 主要思想:每次渲染几条,利用定时器不断地在页面添加渲染的节点
2.利用定时器window.requestAnimationFrame()将document.createDocumentFragment()创建的虚拟节点不停的放入dom容器中
讲一下这两个方法:
requestAnimationFrame(),他的作用就是代替定时器做更加流畅高性能的动画,做可以匹配设备刷新率的动画,他解决了定时器做动画时间间隔不稳定的问题(也就是解决定时器做动画不流畅的问题)。他的用法与setTimeout差不多。 比起 setTimeout、setInterval的优势主要有两点:
1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。
createdocumentfragment()方法是用于创建了一个虚拟的节点对象,用于存放一次加载dom对象
**
代码块
<!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>
<body>
<ul id="ul-container"></ul>
<script>
// 每次渲染条数
const renderSize = 100;
// 渲染的次数
const renderCount = Math.ceil(100000/renderSize);
// 获取渲染的dom容器
const container = document.querySelector('#ul-container');
// 已经渲染的次数
let alreadyRender = 0;
function add() {
// 创建虚拟节点
const fragment = document.createDocumentFragment();
for(let i = 0; i < renderCount; i++) {
const li = document.createElement('li');
li.innerHTML = Math.floor(Math.random() *100000);
fragment.append(li);
}
container.appendChild(fragment);
alreadyRender++;
loop();
}
function loop() {
if(alreadyRender<renderCount) {
window.requestAnimationFrame(add)
}
}
loop();
</script>
</body>
</html>