从DOM创建可重用的文档片段

时间:2012-12-27 01:42:38

标签: javascript html dom fragment

我想在架子上有一个文件片段/元素,我连接了许多其他元素。然后,每当我想将这些元素系统中的一个添加到DOM时,我复制片段,添加唯一的DOM ID并附加它。

所以,例如:

var doc = document,
    prototype = doc.createElement(),  // or fragment
    ra = doc.createElement("div"),
    rp = doc.createElement("div"),
    rp1 = doc.createElement("a"), 
    rp2 = doc.createElement("a"),
    rp3 = doc.createElement("a");

ra.appendChild(rp);
rp.appendChild(rp1);
rp.appendChild(rp2);
rp.appendChild(rp3);
rp1.className = "rp1";
rp2.className = "rp2";
rp3.className = "rp3";

prototype.appendChild(ra);

这会创建原型。然后我希望能够复制原型,添加ID,并附加。像这样:

var fr = doc.createDocumentFragment(),
    to_use = prototype; // This step is illegal, but what I want!
                        // I want prototype to remain to be copied again.

to_use.id = "unique_id75";
fr.appendChild(to_use);
doc.getElementById("container").appendChild(fr);

我知道这不合法。我做过小提琴和研究等等,但它没有用。一篇SO帖子建议el = doc.appendChild(el);返回el,但这并没有让我走得太远。

所以......有可能吗?你能创建一个可以重复使用的现成元素吗?或者你是否必须每次都要从头开始构建你想要添加的DOM结构?

基本上我正在寻求性能提升'因为我正在创造成千上万的这些吸盘:)

感谢。

2 个答案:

答案 0 :(得分:19)

使用Node.cloneNode

var container = document.getElementById('container');

var prototype = document.createElement('div');
prototype.innerHTML = "<p>Adding some <strong>arbitrary</strong> HTML in"
 +" here just to illustrate.</p> <p>Some <span>nesting</span> too.</p>"
 +"<p>CloneNode doesn't care how the initial nodes are created.</p>";

var prototype_copy = prototype.cloneNode(true);

prototype_copy.id = 'whatever'; //note--must be an Element!

container.appendChild(prototype_copy);

速度提示

您希望最小化三种操作:

字符串解析

使用innerHTML时会发生这种情况。当您单独使用它时,innerHTML速度很快。由于所有这些DOM方法调用的开销,它通常比等效的手动DOM构造更快。但是,您希望将innerHTML保留在内部循环之外,并且您不希望将其用于追加。 element.innerHTML += 'more html'特别具有灾难性的运行时行为,因为元素的内容越来越大。它还会破坏任何事件或数据绑定,因为所有这些节点都被销毁并重新创建。

因此,为方便起见,使用innerHTML创建“原型”节点,但对于内部循环,使用DOM操作。要克隆原型,请使用不调用解析器的prototype.cloneNode(true)。 (小心克隆原型中的id属性 - 当你将它们附加到文档时,你需要确保它们是唯一的!)

文档树修改(重复appendChild次调用)

每次修改文档树时,都可能触发文档窗口的重绘并更新文档DOM节点关系,这可能很慢。而是将您的追加批量添加到DocumentFragment并将其仅附加到文档DOM一次。

节点查找

如果您已经有一个内存中的原型对象并希望修改它的一部分,那么无论是使用DOM遍历,getElement*还是{{1},您都需要导航DOM以查找和修改这些部分。 }}

通过在创建原型时保留对要修改的节点的引用,使这些搜索不在内部循环中。然后,只要您想要克隆原型的几乎相同的副本,请修改已经引用的节点,然后克隆已修改的原型。

示例模板对象

对于它来说,这是一个基本的(可能是快速的)模板对象,说明了querySelector*和缓存节点引用的使用(减少了字符串解析和节点查找的使用)。

为它提供一个带有类名和cloneNode属性的“原型”节点(或字符串)。类名成为文本内容替换的“插槽”; data-attr="slotname attributename"的元素成为属性名称设置/替换的插槽。然后,您可以使用已定义的插槽的新值为data-attr方法提供对象,并且您将获得已完成替换的节点的克隆。

示例用法位于底部。

render()

答案 1 :(得分:2)

如果innerHTML不快,我会感到震惊。像lo-dash或doT提供的预编译模板似乎是一种很好的方式!

看看这个简单的例子: http://jsperf.com/lodash-template

它表明,使用lo-dash的预编译模板,可以获得300,000 ops / sec的相当复杂的模板。对我来说似乎很快,而且清洁JS。

显然,这只是问题的一部分。这会生成HTML,实际上插入HTML是另一个问题,但是再次,innerHTML似乎赢得了cloneNode和其他基于DOM的方法,并且通常代码更清晰。 http://jsperf.com/clonenode-vs-innerhtml-redo/2

显然你可以把这些基准值得一试。真正重要的是你的实际应用程序。但我建议尝试多种方法,然后自己进行基准测试,然后再下定决心。

注意:许多关于JSPerf模板的基准测试都做错了。他们在每次迭代时重新编译模板,这显然会慢一些。