在 JavaScript 中创建大型静态 DOM 元素的最佳方法?
Posted
技术标签:
【中文标题】在 JavaScript 中创建大型静态 DOM 元素的最佳方法?【英文标题】:Best way to create large static DOM elements in JavaScript? 【发布时间】:2012-03-25 18:36:01 【问题描述】:我的一个 JS 小部件需要经常创建和添加到 DOM 中的许多元素。他们永远不会改变。
因此,一种选择是将 html 本身作为字符串存储在 JS 中,并使用 JQuery 从字符串中创建元素,然后将其附加到文档中:
var elements = "<div><table><tr><td>1</td><td>2</td></tr></table></div>";
function create()
return $(elements);
$("body").append(create());
另一种选择是编写一个函数,该函数将多次使用 document.createElement("div") 或 $("
") 来构建元素,在需要时将它们相互附加,然后附加到文件:function create()
return $("<div>").append($("<table>")......
$("body").append(create());
在第一种情况下,我有一个实际上是 HTML 的大 JS 字符串。在第二种情况下,我有一段笨拙的 JS,它实际上代表了 HTML。
其中一个有(缺点)优点吗?有没有更好的解决方案我没有想到?
【问题讨论】:
这听起来像是 JS 模板引擎的工作,例如 handlebarsjs.com — 不幸的是,我对它的了解还不够,无法为您提供真正的解决方案,但我认为它可能会有所帮助。 @Nix 谢谢,在寻找一种以合理且可维护的方式创建复杂 DOM 元素的方法时偶然发现了这一点。 handlebars.js 正是我所需要的。 :) 【参考方案1】:详细解析 JS 中创建 DOM 的 3 种常用方式及最佳方法。
我将提供 3 种创建大型 DOM 的方法及其优缺点,当然还有最优化的大型 DOM 创建方法以及原因。 底线是在 js 中创建 DOM 时,原生 JS 和 DOM 方法是你的朋友,除非没有其他方法(这不太可能),否则不要使用 Jquery。
比较测试数据:创建了 400 行 5 列并附加到 DOM。 testData 是您从后端以 json 形式获取的用于创建表的对象列表。
附上不同浏览器的执行时间测试结果快照HTML
<div id="employeeListContainer1"></div>
<table id="employeeList2">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Title</th>
<th>ID</th>
<th>Department</th>
</tr>
</thead>
</table>
第一种方式:字符串连接(就跨浏览器的性能而言,最优化的方式)
var tableElementContainer1 = document.getElementById("employeeListContainer1");
var temptableHolder = '<table><thead><tr><th>First Name</th><th>Last Name</th><th>Title</th><th>ID</th><th>Department</th></tr></thead><tbody>';
for(var i=0,len=testData.length; i<len; i++)
temptableHolder += '<tr><td>' + testData[i].firstName + '</td><td>' + testData[i].lastName + '</td><td>' + testData[i].title
+ '</td><td>' + testData[i].id + '</td><td>' + testData[i].department + '</td></tr>';
temptableHolder += '</tbody></table>';
tableElementContainer1.innerHTML = temptableHolder ;
优点:
Firefox/Chrome/IE/Safari 上最快的执行时间(浏览器之间为 3 到 5 毫秒)。通过 performance.now() 和 console.time() API 测量。缺点:
当列数更多并且您需要设置大量属性时,使用字符串可能会变得不那么困难,并且不太容易成立。第二种方法:Native Js document.createElement()(就跨浏览器的性能而言,这是第二种最佳方法)
var tableBody = document.createElement('tbody');
var tableElement2 = document.getElementById("employeeList2");
for(var i=0,len=testData.length; i<len; i++)
tableRow = document.createElement("tr");
for(var k in testData[i])
rowCell = document.createElement("td");
rowCell.appendChild(document.createTextNode(testData[i][k]));
tableRow.appendChild(rowCell);
tableBody.appendChild(tableRow);
tableElement2.appendChild(tableBody);
优点:
在 Firefox/Chrome/Safari 中第二快的执行时间(跨浏览器为 5 到 12 毫秒)。通过 performance.now() 和 console.time() API 测量。 比第一种方法更站得住脚缺点:
在 IE 浏览器中执行时间更长,90+ 毫秒第三种方式:使用 Jquery 创建 DOM(我的建议是不要使用它)
var tableBody = $('<tbody></tbody>');
var tableElement2 = document.getElementById("employeeList2");
for(var i=0,len=testData.length; i<len; i++)
tableRow = $("<tr></tr>");
for(var k in testData[i])
rowCell = $("<td></td>");
rowCell.append(testData[i][k]);
tableRow.append(rowCell);
tableBody.append(tableRow);
tableElement2.append(tableBody);
优点:
易于在元素上添加属性/类/样式,易于阅读且易于维护。缺点:
所有浏览器的执行时间最差(220 毫秒到 330 毫秒),最慢的数字在 IE 中【讨论】:
【参考方案2】:注意:如果您讨厌阅读,请查看下面的摘要以获得最终答案
也许你真的不需要在 jQuery 的帮助下创建它们。
如果该 html 的结构很复杂(因此使用 document.createElement 方法将是一种矫枉过正)我会选择innerHTML
属性。
// somewhere in your code, preferably outside of global scope
var div = document.createElement('div')
div.id = 'mycustomdiv'
document.getElementsByTagName('body')[0].appendChild(div);
// assuming elements contains string of html with your elements
div.innerHTML = elements;
这样可以避免(再次假设)在 jQuery 对象中创建和包装元素的不必要开销。
更新:测试最快的方法是什么http://jsperf.com/creating-complex-elements。该测试证实,当您试图压缩最后一点性能时,会恢复到原生 javascript 和经典 DOM 操作。
更新 2。为了调查为什么 Firefox 10 上的 innerHTML 方法 在将完整字符串传递给 jQuery.append 时会产生如此糟糕的结果,我查看了 jQuery 源代码。
事实证明(在 jQuery 1.7.1 中),他们正在使用另一种方法来创建 dom 元素,即利用 document.createDocumentFragment(当然对于没有适当支持的浏览器有一些后备)。
DocumentFragment 是 DOM 节点。它们绝不是主 DOM 树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到 DOM 树。在 DOM 树中,文档片段被其所有子级替换。
由于文档片段在内存中而不是主 DOM 树的一部分,因此向其添加子级不会导致页面重排。
假设 createDocumentFragment 可用,就脚本的整体跨浏览器性能而言,它是最好的方法。
所以,总结一下:
我的立场是正确的。 如果您在创建新的 DOM 元素时希望在不同浏览器中获得最佳性能,请关注文档片段(如果您不想自己处理各种极端情况,请使用 jQuery)。
有关 documentFragment 的更多信息,请查看 John Resig 博客 http://ejohn.org/blog/dom-documentfragments/ 上的这篇文章
【讨论】:
+1 表示最后的摘要。我认为你应该加粗,并且只加粗。 @WTK,我认为只有在每 25 次插入后清除子级的测试才是公平的,因为在进行最后一次测试时,DOM 被严重污染并且浏览器已经在哭泣。 可能是这样的jsperf.com/dquery-vs-jquery-inserting-dom-elements【参考方案3】:你已经自己回答了。
编辑:删除了错误的样本。
或者还有另一种选择,您可以将 HTML 直接放入隐藏 div 中的当前 html 中,如下所示:
<div id="hiddenContainer" style="display:none;">
<div><table><tr><td>1</td><td>2</td></tr></table></div>
</div>
然后在jquery中,就可以阅读了:
var elements = $("#hiddenContainer").html()
【讨论】:
JS 中没有(正确的)多行字符串。 @Flo 你能详细说明一下吗?还是链接? 我喜欢这个选项来创建一个隐藏的对象,选择它,克隆它,修改它然后附加它。使它很容易取消隐藏以进行样式设置,易于维护 IMO 及其选项我将最终使用。我现在还不需要最优化的性能,而不是 MVP.... 当产品启动并运行时可能会出现.. 感谢@Jiri 的建议。【参考方案4】:如果您正在寻找性能,我会坚持使用第一个版本,因为在第二个版本中,每次您调用 $('<div>')
或 $('<table>')
您正在创建一个新的 jQuery 对象,然后调用 .append()
这也是另一个方法调用你会的。
我会选择第一个。
【讨论】:
但是它比让 JQuery 解析一个巨大的字符串更快吗?我想那也可能很贵...... 是的,我不确定,这取决于您要创建多少元素!但是使用 ajax 并不是一个好主意,因为那样会更快地减慢它的速度。但是你为什么要在那个函数中返回一个 jquery 对象呢?返回字符串,它也可以工作!【参考方案5】:您可以尝试对静态 HTML 块进行 AJAX 提取,而不是将其存储在页面本身中。它还可以让您更灵活地选择将来要插入的块类型。
或者(这只是一个随机的想法,并没有很好地充实),您可以将“结构”存储为 JSON 数据,然后动态解析它。对于<div><div><span>Text here</span></div></div>
,可能类似于"div": "div": "span": "Text here"
。不过,我仍然会使用 AJAX。 :)
【讨论】:
没想到将它存储在服务器上...我想我可以为它制作一个 JSP 片段并使用 AJAX 检索它。嗯..以上是关于在 JavaScript 中创建大型静态 DOM 元素的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 Angular 指令设置为在 Javascript 中创建的 DOM 元素