当前位置: 首页 > news >正文

跟着 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 交互基础、全站导航开发等硬核内容,带你从新手快速进阶,轻松搞定前端开发!

http://www.jsqmd.com/news/797555/

相关文章:

  • 2026年淮安区域再生资源回收公司最新推荐榜:各类废品回收/废旧物资回收/再生资源回收/废金属回收/废旧家电回收/二手设备回收/废旧木材回收 - 海棠依旧大
  • 08-mcp-tool-calling MCP 工具调用:让大模型连接外部工具服务
  • Translumo终极指南:3分钟掌握免费实时屏幕翻译工具,打破游戏与视频语言壁垒
  • 我的世界基岩版手机版(光影材质包大全)下载国际服集合下载分享
  • 【2026年5月下旬全国各地学术会议推荐】人工智能、教育管理、数据挖掘、电力系统、数字伦理、计算机视觉、图像处理、信息安全、生物医学、机电一体化、土木建筑、物联网、航空航天工程、深度学习等多主题可选!
  • 2026年新疆抖音买单服务商最新推荐榜:抖音买单/抖音聚合支付/乌鲁木齐抖音买单 - 海棠依旧大
  • 2026年碳带厂家推荐排行榜:高温碳带、吊牌碳带、水洗碳带、混合基碳带、耐刮碳带优质之选! - 速递信息
  • STM32H750 双外部Flash IAP升级实战:从内存分配到安全校验
  • NPK文件解包终极指南:如何快速提取网易游戏资源
  • 2026实验室天平|工业天平|防爆天平|电子天平|分析天平哪家好?口碑+售后+性价比盘点 - 品牌推荐大师1
  • 高级java每日一道面试题-2025年12月09日-实战篇[Docker]-如何配置 Docker 的日志驱动?有哪些日志驱动可选?
  • Steam成就管理神器:3步解锁你错过的游戏成就
  • Vercel 开源 Open Agents:把 Claude Code 搬上云,关机也能干活
  • CVNets模型部署实战:生产环境下的最佳实践
  • 先睹为快 | 2026年6月国际学术会议一览表
  • TrollInstallerX终极指南:iOS 14-16.6.1设备快速安装TrollStore的完整教程
  • 2026年淮安洪泽区域再生资源回收优质机构推荐:洪泽区高良涧钱天才废品回收站,覆盖废旧金属、塑料、纸品等全品类回收,以合规经营助力绿色循环 - 海棠依旧大
  • STM32F407驱动24C系列EEPROM避坑指南:从24C01到24C512,一个通用程序搞定所有(附KEIL工程)
  • 聚焦仰睡人群核心需求!6款乳胶枕实测对比,强支撑、无异味,选对枕头护颈椎 - 品牌种草官
  • 2026湖南主任医师评审培训哪个机构靠谱?大数据筛选出3家黑马机构 - 医考机构品牌测评专家
  • Mac上Homebrew安装Gradle后,IDEA配置总失败?可能是这个路径没选对
  • SVG编辑器是什么?公众号SVG发布后如何修改?2026新手公众号SVG怎么使用完整指南推荐3个 - 速递信息
  • 【过程控制实践】矩形脉冲响应曲线法:从理论到Python可视化的完整实现
  • 苏南地区私立复读学校综合实力排行实测盘点 - 速递信息
  • CANN/ops-math reduce_min算子
  • Wat完整使用教程:从基础语法到高级修饰符
  • 从基础到实战:深入解析Matlab中abs函数的应用场景与性能考量
  • 如何快速突破百度网盘限速:开源工具的完整指南
  • 售后无忧首选!2026螺旋输送机厂家推荐排行 售后榜 多行业通用 - 极欧测评
  • 别再只做应力分析了!用ABAQUS模拟土壤固结、药物扩散的完整流程与避坑指南