27

I have the following javascript:

// create a new article tag
var elem = document.createElement('article');

// append the article to the comments list
document.querySelector('#comments-list').appendChild(elem);

I want to set the content of the article, and add some classes to it too so I'm looking at two ways of doing this:

// Option 1
// set the content using .innerHTML()
// and add the classes manually to the classList 
elem.innerHTML = "This is the comment";
elem.classList.add('comment'); 

// Option 2
// set the content and classes in one go using .outerHTML()
elem.outerHTML = "<article class='comment'>This is the comment</article>";

Both work great, but I notice that .innerHTML needs to be called before the element is appended to the DOM, wheras outerHTML needs to be called after it added to the DOM.

I prefer the second option because I'm actually rendering Rails partials in this javascript file, and there's a nuanced case where it is preferable.

My question is whether one of these techniques is better than the other? Is is a problem to add an element to the DOM and then change it's HTML afterwards? Or is it better from a perfomance standpoint to set innerHTML before writing to the DOM?

stephenmurdoch
  • 34,024
  • 29
  • 114
  • 189

3 Answers3

21

Taken from the MDN-site :

innerHTML

  • The Element.innerHTML property sets or gets the HTML syntax describing the element's descendants.

Note: If a <div>, <span>, or <noembed> node has a child text node that includes the characters (&), (<), or (>), innerHTML returns these characters as &amp, &lt and &gt respectively. Use Node.textContent to get a correct copy of these text nodes' contents.

outerHTML

The outerHTML attribute of the element DOM interface gets the serialized HTML fragment describing the element including its descendants. It can be set to replace the element with nodes parsed from the given string.

innerHTML vs. outerHTML :

Use innerHTML as default. This replaces only the content (if using i.e. "=") inside the current element referred to. If you are using outerHTML, then the element referred to will also be replaced.

Demo:

var h20 = document.getElementById("h20"),
    h21 = document.getElementById("h21");
var ran = false;

console.log("'h20' innerHTML (1) =", "'" + h20.innerHTML + "'", "outerHTML (1) =", "'" + h20.outerHTML + "'");
console.log("'h21' innerHTML (1) =", "'" + h21.innerHTML + "'", "outerHTML (1) =", "'" + h21.outerHTML + "'");

document.getElementById("button").onclick = evt => {
    if (ran) return false;
    
    h20.innerHTML = "<div>innerHTML</div>";
    h21.outerHTML = "<div>outerHTML</div>";
    
    console.log("'h20' innerHTML (2) =", "'" + h20.innerHTML + "'", "outerHTML (2) =", "'" + h20.outerHTML + "'");
    console.log("'h21' innerHTML (2) =", "'" + h21.innerHTML + "'", "outerHTML (2) =", "'" + h21.outerHTML + "'");
    
    ran = true
}
<button id="button">innerHTML vs. outerHTML</button>
<br>
<h2 id="h20"></h2>
<h2 id="h21"></h2>
ravo10
  • 895
  • 9
  • 18
  • "This replaces only the content (if using i.e. "=") inside the current element reffered (sic) to." - this isn't correct - it destroys and recreates the element entirely - hence it destroying any event handlers, etc, that may have been registered on it. – Fraser Oct 27 '17 at 11:41
  • no your statement "This replaces only the content (if using i.e. "=") inside the current element reffered (sic) to." - the only bit not copied - is factually incorrect - it doesn't only replace the content. It destroys the container too. I shall vote as I see fit - please feel free to "report" anything you feel. – Fraser Oct 27 '17 at 13:40
  • @Fraser no, it does not? `outerHTML` targets self, so it it also destroy/replaces the *container* (self) in the process when being replaced. `innerHTML` replaces all the children, and does not destroy the container (self). If you have an event handler added to an element which you use `innerHTML` on, the event handler will **not** be destroyed. It will continue to work, since the element is still there. – ravo10 Oct 27 '17 at 13:57
  • Modifying innerHTML causes the content to be re-parsed and DOM nodes to be recreated, losing any handlers you have attached. – Fraser Oct 27 '17 at 14:05
  • @Fraser yes! But you were not talking about the content! You were referring to the *container*. When an element is replaced/destroyed it does include being *re-parsed and DOM nodes* of course! I have not stated anything else. – ravo10 Oct 27 '17 at 14:10
4

I'd say both are probably not what you want, use textContent or else whatever property handles the text for the element.

elem.textContent = "This is the comment";
elem.classList.add("comment"); 

innerHTML parses content as HTML and completely destroys the element and recreates it, it also destroys any event handlers, etc that might be registered for the element. With outerHTML the element set will still hold a reference to the original element (if any).

So both have possible unintended side-effects vs the correct property to set text content.

Fraser
  • 15,275
  • 8
  • 53
  • 104
  • Thanks @Fraser, I'm now using textContent. – stephenmurdoch Oct 06 '18 at 13:38
  • 1
    Thank you, this answer makes much more sense within the context of the question. – spacing Sep 30 '20 at 15:52
  • @Fraser - would altering the innerHtml of an element with `id=1` still retain handlers associated with that element, but outerHTML completely destroys those handlers? – BenKoshy Aug 11 '21 at 23:49
  • @BKSpurgeon Not quite sure what you are asking...if all you want to do is to update the elements ID then you can just do `foo.id = 1` and there is no need for `innerHtml`. Also it is the other way around - `innerHtml` will remove any handlers associated with the element because it destroys and recreates the element. `outerHTML ` will not - but 99.99% of the time you don't want to use either of them unless there is a very good reason to. – Fraser Aug 12 '21 at 07:15
  • 1
    @Fraser - thx for your response. My understanding was that replacing innerHTML on an element will retain any event handlers associated with a particular element while just changing its contents only. But you seem to be saying that it is incorrect? – BenKoshy Aug 12 '21 at 10:57
  • 1
    @BKSpurgeon - yes It destroys the element and therefore any handlers or other data that may have been set on a property of the element. The only exception are attributes which are retained. See: https://stackoverflow.com/questions/5113105/manipulating-innerhtml-removes-the-event-handler-of-a-child-element – Fraser Aug 12 '21 at 14:19
  • @Fraser excellent! my communication was not clear. Thank you for your clear explanation. Handlers are retained on the parent element, but destroyed on all child elements: https://gist.github.com/benkoshy/01cf4310929b9fa822263bcc97ee6817 – BenKoshy Aug 12 '21 at 19:57
0

According to the OP questions, its comments, and the one good answer, in this instance you are better off using elem.outerHTML because you are/can pre-parse your input in Rails.

If you were to move decision making to the JavaScript side, good coding practice would dictate creating all nodes by hand. If you are processing 100's of element inserts then you will notice a difference in speed (if you were to benchtest both solutions).

Paul Wratt
  • 51
  • 5