当需要插入几万个 DOM 元素时,如果直接操作 DOM,会触发浏览器的重排和重绘,导致页面卡顿甚至失去响应。为了避免这种情况,可以采用以下几种策略:
1. 使用文档片段(DocumentFragment)
文档片段是一种轻量级的 DOM 对象,它存在于内存中,不会插入到实际的 DOM 树中。因此,在文档片段上进行 DOM 操作不会触发重排和重绘,操作完成后再将文档片段一次性插入到实际的 DOM 中,这样只会触发一次重排和重绘。
<!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>
<body>
<div id="container"></div>
<script>
const container = document.getElementById('container');
const fragment = document.createDocumentFragment();
// 模拟插入几万个 DOM 元素
for (let i = 0; i < 50000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
// 将文档片段一次性插入到实际的 DOM 中
container.appendChild(fragment);
</script>
</body>
</html>
2. 分批插入
将大量的 DOM 插入操作分成多个小批次,每次插入一小部分元素,然后使用 requestAnimationFrame
或 setTimeout
来控制插入的时间间隔,让浏览器有时间进行渲染,避免一次性插入过多元素导致卡顿。
<!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>
<body>
<div id="container"></div>
<script>
const container = document.getElementById('container');
const totalItems = 50000;
const batchSize = 100;
let currentIndex = 0;
function insertBatch() {
const fragment = document.createDocumentFragment();
for (let i = 0; i < batchSize && currentIndex < totalItems; i++) {
const div = document.createElement('div');
div.textContent = `Item ${currentIndex}`;
fragment.appendChild(div);
currentIndex++;
}
container.appendChild(fragment);
if (currentIndex < totalItems) {
requestAnimationFrame(insertBatch);
}
}
insertBatch();
</script>
</body>
</html>
3. 虚拟列表
如果插入的 DOM 元素是用于展示列表数据,且列表数据量非常大,可以使用虚拟列表技术。虚拟列表只渲染当前可见区域内的元素,当用户滚动列表时,动态更新可见区域内的元素,从而减少 DOM 元素的数量,提高页面性能。
以下是一个简单的虚拟列表示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Virtual List</title>
<style>
#list-container {
height: 300px;
overflow-y: auto;
border: 1px solid #ccc;
}
.list-item {
height: 30px;
line-height: 30px;
padding: 0 10px;
border-bottom: 1px solid #eee;
}
</style>
</head>
<body>
<div id="list-container"></div>
<script>
const container = document.getElementById('list-container');
const itemHeight = 30;
const totalItems = 50000;
const visibleItems = Math.floor(container.offsetHeight / itemHeight);
function renderList(startIndex) {
container.innerHTML = '';
for (let i = startIndex; i < startIndex + visibleItems; i++) {
if (i < totalItems) {
const div = document.createElement('div');
div.classList.add('list-item');
div.textContent = `Item ${i}`;
container.appendChild(div);
}
}
}
container.addEventListener('scroll', function () {
const scrollTop = this.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
renderList(startIndex);
});
renderList(0);
</script>
</body>
</html>
通过以上方法,可以有效避免在插入大量 DOM 元素时导致页面卡顿的问题。