"innerHTML += ..." vs "appendChild(txtNode)"

77
问题是:比较使用innerHTML拼接和将文本节点附加到现有节点。在幕后发生了什么?
至今为止,我的想法如下: - 我猜两者都会导致“ReFlow”。 - 后者(附加文本节点)据我所知,还会导致DOM的完全重建(正确吗?它们俩都这样做吗?)。 - 前者似乎还有一些其他不良副作用,例如使对子节点的先前保存的引用不再指向“当前DOM”/“子节点的正确版本”。相比之下,当附加子元素时,引用似乎保持完好无损。为什么会这样?
希望您能为我澄清这个问题,谢谢!

很久以前,FF在appendChild中存在一个bug,导致注入后未选择<select>元素中的选定<option>。因此,必须使用innerHTML,因为构建基于标记字符串的DOM的HTML解析器没有相同的bug! - Ashwin Prabhu
2个回答

114
后者(使用appendChild)并不会导致DOM的完全重建,甚至不会重建目标元素中的所有元素/节点。
前者(设置innerHTML)确实会导致目标元素内容的完全重建,如果你正在添加内容,这是不必要的。
通过innerHTML += content 进行添加会使浏览器遍历元素中的所有节点,构建一个HTML字符串以提供给JavaScript层。然后你的代码将文本附加到它上面并设置innerHTML,导致浏览器删除目标中的所有旧节点,重新解析所有HTML,并构建新节点。因此,在这个意义上,它可能不太高效。(但解析HTML就是浏览器所做的事情,而且他们真的非常快。)
设置innerHTML确实会使你持有的目标元素内部元素的任何引用无效 - 因为那些元素不再存在了,当你设置innerHTML时,你删除了它们,然后放入了新的元素(看起来非常相似)。
简而言之,如果你正在添加内容,我会使用appendChild(或insertAdjacentHTML,见下文)。如果你正在替换,使用innerHTML比通过DOM API自己创建树更好的选择有很多合理的情况(速度是其中最主要的)。
最后,值得一提的是还有另外几种选择:
  • append是DOM的一个相对较新的添加方式(但除了真正过时的浏览器外,它有出色的支持)。它将一个或多个项目附加到元素上,这些项目可以是节点、元素或定义节点和元素的HTML字符串。与appendChild不同,它支持HTML字符串以及节点,并且还支持多个参数。对于您的用例,它比列表中的下一个选项parent.append(htmlString)更少繁琐。
  • insertAdjacentHTML将您提供的节点和元素作为HTML字符串插入到元素内部或旁边。您可以使用它附加到元素:theElement.insertAdjacentHTML("beforeend", "the HTML goes here");第一个参数是要放置HTML的位置;您的选择是:
    • "beforebegin"(在元素外部,在其前面)
    • "afterbegin"(在元素内部,在开头)
    • "beforeend"(在元素内部,在结尾处)
    • "afterend"(在元素外部,在其后面) 请注意,"afterbegin""beforeend"插入到元素本身中,而"beforebegin""afterend"插入到元素的父元素中。浏览器支持基本上是普遍的
  • insertAdjacentTextinsertAdjacentHTML类似,只是它插入文本节点(文本不被视为HTML)。浏览器支持基本上是普遍的

@TK:浏览器支持如何?哪种技术更有可能在旧版和/或移动浏览器上运行? - skaffman
7
在当今时代,我期望几乎所有你所使用的浏览器都支持这两种方法。虽然innerHTML不是标准化的方法,但几乎被所有工具库(如Prototype和jQuery)广泛应用。而appendChild作为DOM最早的一代之一,也会被支持。需要注意的是,innerHTML还存在一些限制和奇怪的问题,例如在某些浏览器中无法通过设置TR元素的innerHTML来替换表格行。而IE则会在使用innerHTML时删除前导空格,此外,表单元素也可能会带来挑战等等。 - T.J. Crowder
4
如果你正在使用 innerHTML,不要在循环内使用它。这样做非常低效,因为你会多次启动解析器。我曾经看过像这样的循环:for (var i=0; i < arr.length; i++) { node.innerHTML += arr[i]; }。相反,应该使用一个变量,在循环外设置 innerHTML。+1 给我 :-) - Andy E
3
我之前的陈述已经过时了。innerHTML已经被标准化:http://dev.w3.org/html5/spec/embedded-content-0.html#dom-innerhtml - T.J. Crowder
@skaffman,我查看了这个链接:http://andrew.hedges.name/experiments/innerhtml/,发现对于iOS来说最好使用innerHTML。 - pyramation
1
@pyramation:设置 innerHTML 和读取它之间有很大的区别。正如我上面所说,当你读取它时,浏览器必须通过所有节点,建立它们的HTML表示形式。当在页面上放置新内容时,最好添加一个元素并设置其 innerHTML(因为阅读HTML字符串基本上是浏览器所做的)。但我绝不建议使用 +=innerHTML - T.J. Crowder

8

1
值得一提的是,这个要点代表了在现有的innerHTML中追加内容时的性能,而不是替换整个节点的内容。 - Hubert
请勿使用innerHTML!在使用之前,请查看已接受的答案。 - Bruno Casali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接