如何将HTML字符转换为DOM节点并动态添加到文档中详解


Posted in Javascript onAugust 19, 2018

前言

将字符串动态转换为DOM节点,在开发中经常遇到,尤其在模板引擎中更是不可或缺的技术。

字符串转换为DOM节点本身并不难,本篇文章主要涉及两个主题:

     1 字符串转换为HTML DOM节点的基本方法及性能测试

     2 动态生成的DOM节点添加到文档中的方法及性能测试

本文的示例: 有如下代码段

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
</head>
<body>
 <div id='container'>
<!-- 动态添加div 
 <div class='child'> XXX</div>
 -->
 </div>
</body>
</html>

任务是编写一个JavaScript函数,接收一个文本内容,动态生成一个包含该文本的div,返回该Node。下面话不多说了,来随着小编一起看看详细的介绍吧。

1.1 动态创建Node

1.1.1 innerHTML

第一种方法,我们使用document.createElement方法创建新的元素,然后利用innerHTML将字符串注入进去,最后返回firstChild,得到动态创建的Node。

<script>
  function createNode(txt) {
   const template = `<div class='child'>${txt}</div>`;
   let tempNode = document.createElement('div');
   tempNode.innerHTML = template;
   return tempNode.firstChild;
  }
  const container = document.getElementById('container');
  container.appendChild(createNode('hello'));
 </script>

下面我们看第二种方法

1.1.2 DOMParser

DOMParser 实例的parseFromString方法可以用来直接将字符串转换为document 文档对象。有了document之后,我们就可以利用各种DOM Api来进行操作了。

function createDocument(txt) {
   const template = `<div class='child'>${txt}</div>`;
   let doc = new DOMParser().parseFromString(template, 'text/html');
   let div = doc.querySelector('.child');
   return div;
  }
  
  const container = document.getElementById('container');
  container.appendChild(createDocument('hello'));

1.1.2 DocumentFragment

DocumentFragment 对象表示一个没有父级文件的最小文档对象。它被当做一个轻量版的 Document 使用,用于存储已排好版的或尚未打理好格式的XML片段。最大的区别是因为DocumentFragment不是真实DOM树的一部分,它的变化不会引起DOM树的重新渲染的操作(reflow) ,且不会导致性能等问题。

利用document.createRange().createContextualFragment方法,我们可以直接将字符串转化为DocumentFragment对象。

function createDocumentFragment(txt) {
   const template = `<div class='child'>${txt}</div>`;
   let frag = document.createRange().createContextualFragment(template);
   return frag;
  }

  const container = document.getElementById('container');
  container.appendChild(createDocumentFragment('hello'));

这里要注意的是我们直接将生成的DocumentFragment对象插入到目标节点中,这会将其所有自己点插入到目标节点中,不包含自身。我们也可以使用

frag.firstChild

来获取生成的div。

1.1.3 性能测试

下面我们来简单比对下上面三种方法的性能,只是测试生成单个节点,在实际使用中并不一定有实际意义。

先测试createNode。

function createNode(txt) {
   const template = `<div class='child'>${txt}</div>`;

   let start = Date.now();
   for (let i = 0; i < 1000000; i++) {
    let tempNode = document.createElement('div');
    tempNode.innerHTML = template;
    let node = tempNode.firstChild;
   }
   console.log(Date.now() - start);

  }
  createNode('hello');

测试100万个Node生成,用时 6322。

再来测试createDocument。

function createDocument(txt) {
   const template = `<div class='child'>${txt}</div>`;
   let start = Date.now();
   for (let i = 0; i < 1000000; i++) {
    let doc = new DOMParser().parseFromString(template, 'text/html');
    let div = doc.firstChild;
   }
   console.log(Date.now() - start);
  }
 createDocument('hello');

测试100万个Node生成,用时 55188。

最后来测试createDocumentFragment.

function createDocumentFragment(txt) {
   const template = `<div class='child'>${txt}</div>`;
   let start = Date.now();
   for (let i = 0; i < 1000000; i++) {
   let frag = document.createRange().createContextualFragment(template);
   }
   console.log(Date.now() - start);
  }
  createDocumentFragment();

测试100万个Node生成,用时 6210。

createDocumentFragment方法和createNode方法,在这轮测试中不相上下。下面我们看看将生成的DOM元素动态添加到文档中的方法。

1.2.0 批量添加节点

被动态创建出来的节点大多数情况都是要添加到文档中,显示出来的。下面我们来介绍并对比几种常用的方案。
下面我们批量添加的方法都采用createDocumentFragment方法。

1.2.1 直接append

直接append方法,就是生成一个节点就添加到文档中,当然这会引起布局变化,被普遍认为是性能最差的方法。

const template = "<div class='child'>hello</div>";

  function createDocumentFragment() {


   let frag = document.createRange().createContextualFragment(template);
   return frag;
  }
  // createDocumentFragment();
  const container = document.getElementById('container');
  let start = Date.now();
  for (let i = 0; i < 100000; i++) {
   container.appendChild(createDocumentFragment());
  }
  console.log(Date.now() - start);

上面的代码我们测算动态添加10万个节点。结果如下:

如何将HTML字符转换为DOM节点并动态添加到文档中详解

测试1000个节点耗时20毫秒,测试10000个节点耗时10001毫秒,测试100000个节点耗时46549毫秒。

1.2.2 DocumentFragment

上面我们已经介绍过DocumentFragment了,利用它转换字符串。下面我们利用该对象来作为临时容器,一次性添加多个节点。

利用document.createDocumentFragment()方法可以创建一个空的DocumentFragment对象。

const template = "<div class='child'>hello</div>";

    function createDocumentFragment() {


      let frag = document.createRange().createContextualFragment(template);
      return frag;
    }
    // createDocumentFragment();
    const container = document.getElementById('container');
    let fragContainer = document.createDocumentFragment();
    let start = Date.now();
    for (let i = 0; i < 1000; i++) {
      fragContainer.appendChild(createDocumentFragment());
    }
    container.appendChild(fragContainer);
    console.log(Date.now() - start);

测试1000个节点耗时25毫秒,10000个节点耗时2877毫秒,100000个节点浏览器卡死。

1.3 小结

简单了介绍了几种方法,并没有什么技术含量。但是从动态添加节点来看,网上说的DocumentFragment方法性能远远好于直接append的说法在我的测试场景中并不成立。

DocumentFragment正确的应用场景应该是作为虚拟DOM容器,在频繁修改查询但是并不需要直接渲染的场景中。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
aspx中利用js实现确认删除代码
Jul 22 Javascript
js取消单选按钮选中并判断对象是否为空
Nov 14 Javascript
javascript+HTML5的Canvas实现Lab单车动画效果
Aug 07 Javascript
js图片轮播效果实现代码
Apr 18 Javascript
JS判断来路是否是百度等搜索索引进行弹窗或自动跳转的实现代码
Oct 09 Javascript
JS中的数组转变成JSON格式字符串的方法
May 09 Javascript
AngularJS实现图片上传和预览功能的方法分析
Nov 08 Javascript
浅谈针对Vue相同路由不同参数的刷新问题
Sep 29 Javascript
浅谈Node框架接入ELK实践总结
Feb 22 Javascript
微信小程序实现原生步骤条
Jul 25 Javascript
Laravel 如何在blade文件中使用Vue组件的示例代码
Jun 28 Javascript
jenkins自动构建发布vue项目的方法步骤
Jan 04 Vue.js
解决layui前端框架 form表单,table表等内置控件不显示的问题
Aug 19 #Javascript
layui框架table 数据表格的方法级渲染详解
Aug 19 #Javascript
详解微信小程序input标签正则初体验
Aug 18 #Javascript
vue实现键盘输入支付密码功能
Aug 18 #Javascript
记录一篇关于redux-saga的基本使用过程
Aug 18 #Javascript
Vue实现6位数密码效果
Aug 18 #Javascript
JavaScript实现淘宝京东6位数字支付密码效果
Aug 18 #Javascript
You might like
最新版本PHP 7 vs HHVM 多角度比较
2016/02/14 PHP
详解php用curl调用接口方法,get和post两种方式
2017/01/13 PHP
两个select之间option的互相添加操作(jquery实现)
2009/11/12 Javascript
Jquery+WebService 校验账号是否已被注册的代码
2010/07/12 Javascript
javascript获取所有同类checkbox选项(实例代码)
2013/11/07 Javascript
使用jQuery异步加载 JavaScript脚本解决方案
2014/04/20 Javascript
jQuery 复合选择器应用的几个例子
2014/09/11 Javascript
jQuery简单实现遍历数组的方法
2015/04/14 Javascript
学习Angular中作用域需要注意的坑
2016/08/17 Javascript
微信小程序 this和that详解及简单实例
2017/02/13 Javascript
详解vue-router2.0动态路由获取参数
2017/06/14 Javascript
BootStrap 标题设置跨行无效的解决方法
2017/10/25 Javascript
vue.js系列中的vue-fontawesome使用
2018/02/10 Javascript
详解node Async/Await 更好的异步编程解决方案
2018/05/10 Javascript
vue导出html、word和pdf的实现代码
2018/07/31 Javascript
jquery 验证用户名是否重复代码实例
2019/05/14 jQuery
vue项目中全局引入1个.scss文件的问题解决
2019/08/01 Javascript
JavaScript自动生成 年月范围 选择功能完整示例【基于jQuery插件】
2019/09/03 jQuery
微信小程序 函数防抖 解决重复点击消耗性能问题实现代码
2019/09/12 Javascript
对layui初始化列表的CheckBox属性详解
2019/09/13 Javascript
js实现简单页面全屏
2019/09/17 Javascript
微信小程序云函数添加数据到数据库的方法
2020/03/04 Javascript
Python面向对象特殊成员
2017/04/24 Python
Python 中pandas索引切片读取数据缺失数据处理问题
2019/10/09 Python
Python实现基于socket的udp传输与接收功能详解
2019/11/15 Python
PythonPC客户端自动化实现原理(pywinauto)
2020/05/28 Python
python+appium+yaml移动端自动化测试框架实现详解
2020/11/24 Python
CSS3中的display:grid,网格布局介绍
2019/10/30 HTML / CSS
京东全球售:直邮香港,澳门,台湾,美国,澳大利亚等地区
2017/09/24 全球购物
幼教个人求职信范文
2013/12/02 职场文书
行政部岗位职责范本
2014/03/13 职场文书
初中英语演讲稿
2014/04/29 职场文书
个人租房协议书(范本)
2014/10/14 职场文书
2015年高三毕业班班主任工作总结
2015/10/22 职场文书
学校标语口号大全
2015/12/26 职场文书
十大最强水系宝可梦,最美宝可梦排第三,榜首大家最熟悉
2022/03/18 日漫