76

The question is, comparing concatination using innerHTML and appending a text node to an existing node. What is happening behind the scene?

My thoughts around this so far:

  • I'm guessing both are causing a 'ReFlow'.
  • The latter (appending a text node), from what I know, also causes a complete rebuild of the DOM (correct? Are they both doing this?).
  • The former seems to have some other nasty side effects, like causing previously saved references to child nodes to the node I'm modifying innerHTML, to no longer point to 'the current DOM'/'correct version of the child node'. In contrast, when appending children, references seem to stay intact. Why is this?

I'm hoping you people can clear this up for me, thanks!

Jesse W. Collins
  • 139
  • 1
  • 1
  • 12
  • Long long ago FF had a bug in appendChild that caused the selected – Ashwin Prabhu Jun 11 '19 at 07:55

2 Answers2

114

The latter (appendChild) does not cause a complete rebuild of the DOM or even all of the elements/nodes within the target.

The former (setting innerHTML) does cause a complete rebuild of the content of the target element, which if you're appending is unnecessary.

Appending via innerHTML += content makes the browser run through all of the nodes in the element building an HTML string to give to the JavaScript layer. Your code then appends text to it and sets innerHTML, causing the browser to drop all of the old nodes in the target, re-parse all of that HTML, and build new nodes. So in that sense, it may not be efficient. (However, parsing HTML is what browsers do and they're really, really fast at it.)

Setting innerHTML does indeed invalidate any references to elements within the target element you may be holding -- because those elements don't exist anymore, you removed them and then put in new ones (that look very similar) when you set innerHTML.

In short, if you're appending, I'd use appendChild (or insertAdjacentHTML, see below). If you're replacing, there are very valid situations where using innerHTML is a better option than creating the tree yourself via the DOM API (speed being chief amongst them).

Finally, it's worth mentioning a couple of other alternatives:

  • append is a relatively recent addition to the DOM (but has excellent support other than truly obsolete browsers). It appends one or more items to the element, where the items can be nodes, elements, or HTML strings defining nodes and elements. Unlike appendChild, it supports HTML strings as well as nodes, and alsos supports multiple arguments. For your use case, it's less cumbersome than the next option in this list: parent.append(htmlString).
  • insertAdjacentHTML inserts nodes and elements you supply as an HTML string into or next to an element. You can append to an element with it: theElement.insertAdjacentHTML("beforeend", "the HTML goes here"); The first argument is where to put the HTML; your choices are:
    • "beforebegin" (outside the element, just in front of it)
    • "afterbegin" (inside the element, at the beginning)
    • "beforeend" (inside the element, at the end)
    • "afterend" (outside the element, just in after it) Note that "afterbegin" and "beforeend" insert into the element itself, whereas "beforebegin" and "afterend" insert into the element's parent. Browser support is basically universal.
  • insertAdjacentText is just like insertAdjacentHTML except that it inserts a text node (the text is not treated as HTML). Browser support is basically universal.
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • @TK: How about browser support? Is one technique more likely than the other to work on older and/or mobile browsers? – skaffman Feb 21 '10 at 11:21
  • 7
    @skaffman: In this day and age, I'd expect both to be available in just about any browser you're working with. `innerHTML` is non-standard but almost universally supported (to give you an idea, both Prototype and jQuery rely on it). `appendChild` is part of the oldest generation of DOM levels, it's going to be there. :-) Note that there are limits and quirks with `innerHTML`. You can't replace a table row by setting the TR element's `innerHTML` on some browsers, for instance. IE will strip leading whitepace when you use it. Form elements can be a challenge. Etc. – T.J. Crowder Feb 21 '10 at 11:25
  • 4
    +1 from me :-) I would say if you're using `innerHTML`, don't use it inside a loop. It's very inefficient to do so because you're starting the parser multiple times. I've seen loops like `for (var i=0; i < arr.length; i++) { node.innerHTML += arr[i]; }`. Instead a variable should be used and `innerHTML` should be set outside the loop. – Andy E Feb 21 '10 at 11:27
  • 3
    And my statement above was out of date. `innerHTML` has been standardized: http://dev.w3.org/html5/spec/embedded-content-0.html#dom-innerhtml – T.J. Crowder Feb 21 '10 at 12:38
  • @skaffman, I checked this link: http://andrew.hedges.name/experiments/innerhtml/ and found that for iOS it's better to use innerHTML – pyramation Oct 13 '11 at 06:48
  • 1
    @pyramation: There's a big difference between setting `innerHTML` and reading it. As I said above, when you *read* it, the browser has to spin through all the nodes building up the HTML representation of them. When putting new content on the page, it's almost always best to add an element and set its `innerHTML` (because reading through HTML strings is fundamentally *what browsers do*). But I would never recommend using `+=` with `innerHTML`. – T.J. Crowder Oct 13 '11 at 10:39
8

I've created a small gist with a performance comparison between innerHTML and appendChild.

The last one wins by a wide margin

https://gist.github.com/oliverfernandez/5619180

oliferna
  • 369
  • 1
  • 3
  • 9
  • 1
    It's worth mentioning this gist represents performance when appending to an existing `innerHTML`, not replacing the whole node's contents. – Hubert Mar 14 '18 at 00:46
  • Please, do not use innerHTML ever! See the accepted answer before use it. – Bruno Casali Feb 09 '20 at 02:33