ZetCode

JavaScript DocumentFragment

最后修改于 2025 年 4 月 2 日

在本文中,我们将探讨 JavaScript 中的 document.createDocumentFragment 方法。这个强大的工具通过创建轻量级的文档片段,可以在将它们添加到主 DOM 树之前进行修改,从而帮助优化 DOM 操作。

基本定义

DocumentFragment 是一个最小化的文档对象,它没有父节点。它被用作 Document 的轻量级版本,以与标准文档类似的方式存储由节点组成的文档结构片段。

DocumentFragment 的主要优点是,对它的更改在附加到主文档之前不会触发回流或重绘。这使其成为批量 DOM 操作的理想选择。

基本 DocumentFragment 创建

此示例演示了如何创建和使用基本的 DocumentFragment。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Basic DocumentFragment</title>
</head>
<body>

<div id="container"></div>

<script>
    const fragment = document.createDocumentFragment();
    const p1 = document.createElement('p');
    p1.textContent = 'First paragraph';
    const p2 = document.createElement('p');
    p2.textContent = 'Second paragraph';
    
    fragment.appendChild(p1);
    fragment.appendChild(p2);
    
    document.getElementById('container').appendChild(fragment);
</script>

</body>
</html>

在此示例中,我们创建一个 DocumentFragment,并向其中添加两个段落元素。然后,将该片段作为一个单一操作附加到容器 div 中。

这种方法比直接将每个段落添加到 DOM 更有效,因为它最大限度地减少了回流。片段充当我们节点的临时容器。

批量 DOM 插入

此示例展示了 DocumentFragment 如何优化元素的批量插入。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Batch Insertion</title>
</head>
<body>

<ul id="list"></ul>
<button onclick="addItems()">Add Items</button>

<script>
    function addItems() {
        const fragment = document.createDocumentFragment();
        
        for (let i = 1; i <= 100; i++) {
            const li = document.createElement('li');
            li.textContent = `Item ${i}`;
            fragment.appendChild(li);
        }
        
        document.getElementById('list').appendChild(fragment);
    }
</script>

</body>
</html>

在这里,我们创建 100 个列表项,并将它们添加到 DocumentFragment 中,然后再一次性将它们插入到 DOM 中。这比单独添加每个列表项要有效得多。

没有 DocumentFragment,浏览器会在每次插入后回流。使用它,我们在片段附加到实际 DOM 时只发生一次回流。

模板克隆

此示例演示了将 DocumentFragment 与模板元素一起使用。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Template Cloning</title>
</head>
<body>

<template id="productTemplate">
    <div class="product">
        <h3></h3>
        <p class="price"></p>
    </div>
</template>

<div id="products"></div>
<button onclick="addProducts()">Add Products</button>

<script>
    function addProducts() {
        const template = document.getElementById('productTemplate');
        const fragment = document.createDocumentFragment();
        
        const products = [
            { name: 'Laptop', price: '$999' },
            { name: 'Phone', price: '$699' },
            { name: 'Tablet', price: '$399' }
        ];
        
        products.forEach(product => {
            const instance = template.content.cloneNode(true);
            instance.querySelector('h3').textContent = product.name;
            instance.querySelector('.price').textContent = product.price;
            fragment.appendChild(instance);
        });
        
        document.getElementById('products').appendChild(fragment);
    }
</script>

</body>
</html>

此示例结合了 HTML 模板和 DocumentFragment,以实现高效的 DOM 创建。我们将每个产品的模板内容克隆一份,并在插入之前将其添加到片段中。

模板内容本身就是一个 DocumentFragment,我们会克隆并修改它,然后再添加到我们的主片段中。这种模式对于重复的 UI 元素非常有效。

移动现有节点

此示例展示了如何使用 DocumentFragment 来移动现有的 DOM 节点。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Moving Nodes</title>
</head>
<body>

<div id="source">
    <p>First paragraph</p>
    <p>Second paragraph</p>
    <p>Third paragraph</p>
</div>

<div id="destination"></div>
<button onclick="moveNodes()">Move Paragraphs</button>

<script>
    function moveNodes() {
        const source = document.getElementById('source');
        const fragment = document.createDocumentFragment();
        
        while (source.firstChild) {
            fragment.appendChild(source.firstChild);
        }
        
        document.getElementById('destination').appendChild(fragment);
    }
</script>

</body>
</html>

在这里,我们使用 DocumentFragment 将源 div 的所有子节点移动到目标 div。这比逐个移动节点更有效。

在移动操作过程中,片段会临时保存节点。这项技术在不使用临时变量的情况下重组 DOM 结构时非常有用。

性能比较

此示例演示了使用 DocumentFragment 与直接 DOM 操作之间的性能差异。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Performance Comparison</title>
</head>
<body>

<button onclick="testFragment()">Test Fragment</button>
<button onclick="testDirect()">Test Direct</button>
<div id="results"></div>

<script>
    function testFragment() {
        const start = performance.now();
        const fragment = document.createDocumentFragment();
        
        for (let i = 0; i < 1000; i++) {
            const div = document.createElement('div');
            div.textContent = `Item ${i}`;
            fragment.appendChild(div);
        }
        
        document.body.appendChild(fragment);
        const time = performance.now() - start;
        document.getElementById('results').textContent = 
            `Fragment time: ${time.toFixed(2)}ms`;
    }
    
    function testDirect() {
        const start = performance.now();
        
        for (let i = 0; i < 1000; i++) {
            const div = document.createElement('div');
            div.textContent = `Item ${i}`;
            document.body.appendChild(div);
        }
        
        const time = performance.now() - start;
        document.getElementById('results').textContent = 
            `Direct time: ${time.toFixed(2)}ms`;
    }
</script>

</body>
</html>

此示例比较了使用 DocumentFragment 添加 1000 个元素与直接将它们添加到 DOM 的性能。片段方法要快得多。

性能差异源于减少的回流。每次直接追加都会导致一次回流,而片段方法最终只产生一次回流。

来源

MDN DocumentFragment 文档

在本文中,我们展示了如何在 JavaScript 中使用 document.createDocumentFragment。这项技术对于优化 DOM 操作和提高 Web 应用程序性能至关重要。

作者

我叫 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 JS DOM 教程