news 2026/5/11 18:59:50

跟着 MDN 学 HTML day_38:(DocumentFragment 文档片段接口详解)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跟着 MDN 学 HTML day_38:(DocumentFragment 文档片段接口详解)

一、什么是 DocumentFragment

DocumentFragment 是 DOM 接口中一个非常实用但常常被忽视的特性。它表示一个没有父对象的最小文档对象,可以将其理解为一个轻量版的 Document 对象。与标准的 document 对象类似,DocumentFragment 能够存储由节点组成的文档结构。

DocumentFragment 与常规 DOM 节点最大的区别在于,它不是真实 DOM 树的一部分。这意味着对 DocumentFragment 所做的任何修改都不会触发浏览器的重排和重绘,也不会对页面的性能产生任何负面影响。这一特性使得 DocumentFragment 成为批量 DOM 操作的理想工具。

// 创建一个空的 DocumentFragment 对象constfragment=newDocumentFragment();console.log(fragment.nodeType);// 11console.log(fragment.nodeName);// #document-fragment// 也可以使用 createDocumentFragment 方法constfragment2=document.createDocumentFragment();console.log(fragment2instanceofDocumentFragment);// true

二、构造函数与创建方式

DocumentFragment 提供了标准的构造函数,可以直接通过 new 关键字来创建实例。此外,传统上也可以使用 document.createDocumentFragment 方法。两种方式都能创建出功能完全相同的 DocumentFragment 对象。

// 方式一:使用构造函数constfragmentFromConstructor=newDocumentFragment();// 方式二:使用 createDocumentFragment 方法constfragmentFromMethod=document.createDocumentFragment();// 验证两种方式创建的对象是否相同console.log(fragmentFromConstructorinstanceofDocumentFragment);// trueconsole.log(fragmentFromMethodinstanceofDocumentFragment);// true// 向片段中添加元素constdiv=document.createElement('div');div.textContent='Hello World';fragmentFromConstructor.appendChild(div);console.log(fragmentFromConstructor.childNodes.length);// 1console.log(fragmentFromConstructor.firstChild.textContent);// Hello World

三、属性详解

DocumentFragment 继承自 Node 接口,同时也拥有一些自身特有的属性。这些属性主要用于获取片段中的元素信息,包括子元素的数量、子元素集合以及首尾元素等。

// 创建示例 DocumentFragmentconstfragment=newDocumentFragment();// 添加多个元素for(leti=0;i<5;i++){constdiv=document.createElement('div');div.textContent=`Item${i+1}`;div.id=`item-${i+1}`;fragment.appendChild(div);}// childElementCount - 获取子元素的数量console.log(fragment.childElementCount);// 5// children - 获取所有子元素的实时 HTMLCollectionconstchildren=fragment.children;console.log(children.length);// 5console.log(children[0].textContent);// Item 1// firstElementChild - 获取第一个子元素constfirstChild=fragment.firstElementChild;console.log(firstChild.textContent);// Item 1console.log(firstChild.id);// item-1// lastElementChild - 获取最后一个子元素constlastChild=fragment.lastElementChild;console.log(lastChild.textContent);// Item 5console.log(lastChild.id);// item-5// 向片段中添加文本节点consttextNode=document.createTextNode('这是一段文本');fragment.appendChild(textNode);// childElementCount 只计算元素节点,不计算文本节点console.log(fragment.childElementCount);// 仍然是 5console.log(fragment.childNodes.length);// 6(5个div + 1个文本节点)

四、核心方法详解

DocumentFragment 提供了多个实用的方法,用于查询、添加和操作片段内的元素。这些方法与 Document 和 Element 接口中的方法非常相似,但作用范围仅限于片段内部。

// 创建测试用的 DocumentFragmentconstfragment=newDocumentFragment();constitems=['苹果','香蕉','橙子','葡萄','西瓜'];items.forEach((item,index)=>{constdiv=document.createElement('div');div.textContent=item;div.className='fruit';div.setAttribute('data-id',index+1);fragment.appendChild(div);});// querySelector - 查询第一个匹配的元素constfirstFruit=fragment.querySelector('.fruit');console.log(firstFruit.textContent);// 苹果// querySelectorAll - 查询所有匹配的元素constallFruits=fragment.querySelectorAll('.fruit');console.log(allFruits.length);// 5allFruits.forEach((fruit,index)=>{console.log(`${index}:${fruit.textContent}`);});// getElementById - 通过 ID 获取元素// 注意:需要先为元素设置 IDconsttargetDiv=fragment.querySelector('.fruit');targetDiv.id='special-fruit';constfoundElement=fragment.getElementById('special-fruit');console.log(foundElement.textContent);// 苹果

五、append 和 prepend 方法

append 和 prepend 是 DocumentFragment 提供的两个便捷方法,用于向片段中添加内容。append 方法将内容添加到片段的末尾,而 prepend 方法则将内容添加到片段开头。这两个方法都可以接受多个参数,参数可以是 Node 节点也可以是字符串。

// 创建空的 DocumentFragmentconstfragment=newDocumentFragment();// 使用 append 添加内容fragment.append('第一段文本');fragment.append(document.createTextNode('第二段文本'));fragment.append(document.createElement('span'));console.log(fragment.childNodes.length);// 3console.log(fragment.childNodes[0].textContent);// 第一段文本// 一次性添加多个内容fragment.append('第三段文本',document.createElement('hr'),'第四段文本');console.log(fragment.childNodes.length);// 6// 使用 prepend 在开头添加内容constnewFragment=newDocumentFragment();newFragment.append('原始内容');newFragment.prepend('开头内容');newFragment.prepend(document.createElement('strong'),'加粗内容');console.log(newFragment.childNodes[0].nodeName);// STRONGconsole.log(newFragment.childNodes[1].textContent);// 加粗内容console.log(newFragment.childNodes[2].textContent);// 原始内容

六、实际应用场景与性能优化

DocumentFragment 最经典的用法是作为 DOM 操作的缓冲区。当需要向页面中批量添加大量元素时,如果逐个添加会触发多次重排重绘,严重影响性能。使用 DocumentFragment 可以将所有元素先添加到片段中,然后一次性插入到 DOM 中,这样只触发一次重排重绘。

// 场景一:动态生成列表项// 传统低效的方式constlist=document.getElementById('myList');for(leti=0;i<1000;i++){constli=document.createElement('li');li.textContent=`列表项${i}`;list.appendChild(li);// 触发 1000 次重排}// 使用 DocumentFragment 的高效方式constlist2=document.getElementById('myList2');constfragment=newDocumentFragment();for(leti=0;i<1000;i++){constli=document.createElement('li');li.textContent=`列表项${i}`;fragment.appendChild(li);// 在内存中操作,不触发重排}list2.appendChild(fragment);// 只触发一次重排// 场景二:批量创建复杂结构constcontainer=document.getElementById('container');constcardsFragment=newDocumentFragment();for(leti=0;i<100;i++){constcard=document.createElement('div');card.className='card';consttitle=document.createElement('h3');title.textContent=`卡片标题${i}`;constcontent=document.createElement('p');content.textContent=`这是卡片${i}的内容描述信息`;card.appendChild(title);card.appendChild(content);cardsFragment.appendChild(card);}container.appendChild(cardsFragment);

七、与 Template 元素的配合使用

在 Web Components 开发中,template 元素与 DocumentFragment 有着密切的关系。每个 template 元素都有一个 content 属性,该属性返回一个 DocumentFragment 对象,包含了模板中的内容。这使得我们可以重复使用模板内容来创建多个 DOM 结构。

// 创建 template 元素consttemplate=document.createElement('template');template.innerHTML=`<div class="product-card"> <h2 class="product-title"></h2> <p class="product-price"></p> <button class="buy-btn">购买</button> </div>`;// 获取 template 内容的 DocumentFragmentconsttemplateContent=template.content;console.log(templateContentinstanceofDocumentFragment);// true// 使用模板创建多个产品卡片constproducts=[{name:'笔记本电脑',price:'5999元'},{name:'智能手机',price:'3999元'},{name:'无线耳机',price:'999元'}];constcontainer2=document.getElementById('products-container');constfragment2=newDocumentFragment();products.forEach(product=>{// 克隆模板内容constclone=templateContent.cloneNode(true);constcard=clone.querySelector('.product-card');consttitle=clone.querySelector('.product-title');constprice=clone.querySelector('.product-price');title.textContent=product.name;price.textContent=product.price;fragment2.appendChild(card);});container2.appendChild(fragment2);

八、插入后的行为特点

当 DocumentFragment 被插入到 DOM 树中时,其行为有一个重要的特点:插入的不是片段本身,而是片段的所有子节点。插入完成后,原本的 DocumentFragment 会变为空。理解这一特点对于正确使用 DocumentFragment 非常重要。

// 演示插入后片段变空的行为constfragment=newDocumentFragment();// 添加三个 divfor(leti=0;i<3;i++){constdiv=document.createElement('div');div.textContent=`div${i+1}`;fragment.appendChild(div);}console.log('插入前片段中的子节点数量:',fragment.childNodes.length);// 3// 将片段插入到 body 中document.body.appendChild(fragment);console.log('插入后片段中的子节点数量:',fragment.childNodes.length);// 0// 验证 div 已经被移入 DOMconstbodyDivs=document.querySelectorAll('body > div');console.log('body 中的 div 数量:',bodyDivs.length);// 3// 注意:此时 fragment 已经为空,可以继续复用fragment.appendChild(document.createElement('p'));fragment.firstChild.textContent='这是新添加的内容';console.log('复用后片段中的子节点数量:',fragment.childNodes.length);// 1// 再次插入document.body.appendChild(fragment);console.log('片段再次变空:',fragment.childNodes.length);// 0

九、兼容性与注意事项

DocumentFragment 在现代浏览器中得到广泛支持,使用时需要注意一些细节问题。特别是当片段中包含复杂的事件监听器时,直接插入可能会丢失这些监听器。此外,对于框架开发或者复杂的前端应用,合理使用 DocumentFragment 可以带来明显的性能提升。

// 注意事项一:事件监听器的处理constfragment=newDocumentFragment();constbutton=document.createElement('button');button.textContent='点击我';// 添加事件监听器letclickCount=0;button.addEventListener('click',()=>{clickCount++;console.log(`按钮被点击了${clickCount}`);});fragment.appendChild(button);document.body.appendChild(fragment);// 按钮被插入后点击仍然可以触发事件button.click();// 按钮被点击了 1 次// 注意事项二:插入后原 DOM 元素的引用仍然有效constinsertedButton=document.querySelector('button');console.log(insertedButton===button);// true// 注意事项三:不要在循环中反复操作真实 DOM// 错误示例constcontainer=document.getElementById('container');for(leti=0;i<1000;i++){constdiv=document.createElement('div');container.appendChild(div);// 错误:1000 次重排}// 正确示例constcorrectFragment=newDocumentFragment();for(leti=0;i<1000;i++){constdiv=document.createElement('div');correctFragment.appendChild(div);}container.appendChild(correctFragment);// 正确:1 次重排

DocumentFragment 作为 DOM 操作中重要的性能优化工具,掌握其使用方法对于编写高效的前端代码具有重要意义。在日常开发中,只要涉及批量 DOM 操作,都应该优先考虑使用 DocumentFragment 来减少重排重绘次数,提升页面性能。


想要解锁更多HTML 核心标签实战、前端零基础入门干货、开发避坑全指南吗?
持续关注,后续将更新CSS 布局实战、JavaScript 交互基础、全站导航开发等硬核内容,带你从新手快速进阶,轻松搞定前端开发!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 18:58:43

LeagueAkari:英雄联盟玩家的5大实用自动化工具集

LeagueAkari&#xff1a;英雄联盟玩家的5大实用自动化工具集 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power &#x1f680;. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit LeagueAkari是一款基于英雄联盟LC…

作者头像 李华
网站建设 2026/5/11 18:58:42

告别cURL命令行!用libhv的C++ HTTP客户端模拟Postman发请求(附完整代码)

告别Postman&#xff1a;用libhv构建C原生HTTP客户端的完整实践指南 在API开发与调试过程中&#xff0c;Postman无疑是大多数开发者的首选工具。它友好的图形界面让我们能够快速构造各种HTTP请求&#xff0c;测试接口响应。然而&#xff0c;当我们需要将这些HTTP调用集成到C项目…

作者头像 李华
网站建设 2026/5/11 18:57:34

DuckDuckGo iOS:终极隐私浏览器完全指南,保护你的在线数据安全

DuckDuckGo iOS&#xff1a;终极隐私浏览器完全指南&#xff0c;保护你的在线数据安全 【免费下载链接】iOS DuckDuckGo iOS Application 项目地址: https://gitcode.com/gh_mirrors/ios/iOS 在当今数字时代&#xff0c;隐私保护已成为每位手机用户的基本需求。DuckDuck…

作者头像 李华
网站建设 2026/5/11 18:56:16

3大核心模块解析:Funannotate真核基因组注释实战手册

3大核心模块解析&#xff1a;Funannotate真核基因组注释实战手册 【免费下载链接】funannotate Eukaryotic Genome Annotation Pipeline 项目地址: https://gitcode.com/gh_mirrors/fu/funannotate Funannotate是一款专为真核生物基因组设计的专业注释工具&#xff0c;通…

作者头像 李华
网站建设 2026/5/11 18:55:16

LinkedOM SSR实战:用三重链表构建高性能服务端渲染

LinkedOM SSR实战&#xff1a;用三重链表构建高性能服务端渲染 【免费下载链接】linkedom A triple-linked lists based DOM implementation. 项目地址: https://gitcode.com/gh_mirrors/li/linkedom LinkedOM是一个基于三重链表数据结构的DOM实现&#xff0c;专为服务端…

作者头像 李华