在 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】:

如果您正在寻找性能,我会坚持使用第一个版本,因为在第二个版本中,每次您调用 $('&lt;div&gt;')$('&lt;table&gt;') 您正在创建一个新的 jQuery 对象,然后调用 .append() 这也是另一个方法调用你会的。 我会选择第一个。

【讨论】:

但是它比让 JQuery 解析一个巨大的字符串更快吗?我想那也可能很贵...... 是的,我不确定,这取决于您要创建多少元素!但是使用 ajax 并不是一个好主意,因为那样会更快地减慢它的速度。但是你为什么要在那个函数中返回一个 jquery 对象呢?返回字符串,它也可以工作!【参考方案5】:

您可以尝试对静态 HTML 块进行 AJAX 提取,而不是将其存储在页面本身中。它还可以让您更灵活地选择将来要插入的块类型。

或者(这只是一个随机的想法,并没有很好地充实),您可以将“结构”存储为 JSON 数据,然后动态解析它。对于&lt;div&gt;&lt;div&gt;&lt;span&gt;Text here&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;,可能类似于"div": "div": "span": "Text here"。不过,我仍然会使用 AJAX。 :)

【讨论】:

没想到将它存储在服务器上...我想我可以为它制作一个 JSP 片段并使用 AJAX 检索它。嗯..

以上是关于在 JavaScript 中创建大型静态 DOM 元素的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在javascript类中创建一个静态字段[重复]

如何将 Angular 指令设置为在 Javascript 中创建的 DOM 元素

如何使用 javascript 中的 mouseover 事件删除我在 javascript 中创建的 DOM 节点?

如何在 Javascript 中创建静态字段 [重复]

如何从多个静态库中创建一个静态库?

如何在 ES6 类中创建“公共静态字段”?