Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should y.innerHTML = x.innerHTML; be avoided?

Let's say that we have a DIV x on the page and we want to duplicate ("copy-paste") the contents of that DIV into another DIV y. We could do this like so:

y.innerHTML = x.innerHTML; 

or with jQuery:

$(y).html( $(x).html() ); 

However, it appears that this method is not a good idea, and that it should be avoided.

(1) Why should this method be avoided?

(2) How should this be done instead?


Update:
For the sake of this question let's assume that there are no elements with ID's inside the DIV x.
(Sorry I forgot to cover this case in my original question.)

Conclusion:
I have posted my own answer to this question below (as I originally intended). Now, I also planed to accept my own answer :P, but lonesomeday's answer is so amazing that I have to accept it instead.

like image 313
Šime Vidas Avatar asked Sep 12 '11 19:09

Šime Vidas


People also ask

Why should you avoid innerHTML?

The use of innerHTML creates a potential security risk for your website. Malicious users can use cross-site scripting (XSS) to add malicious client-side scripts that steal private user information stored in session cookies.

What is the disadvantage of innerHTML?

There is no append support without reparsing the whole innerHTML. This makes changing innerHTML directly very slow. innerHTML does not provide validation and therefore we can potentially insert valid and broken HTML in the document and break it.

What is innerHTML and innerHTML?

innerText returns all text contained by an element and all its child elements. innerHtml returns all text, including html tags, that is contained by an element.

What is the difference between innerHTML and appendChild?

Answer : appendChild is used to insert new node in DOM. innerHTML is a property of DOM that allows to replace content of an element with different HTML, which automatically gets parsed into DOM nodes.


2 Answers

This method of "copying" HTML elements from one place to another is the result of a misapprehension of what a browser does. Browsers don't keep an HTML document in memory somewhere and repeatedly modify the HTML based on commands from JavaScript.

When a browser first loads a page, it parses the HTML document and turns it into a DOM structure. This is a relationship of objects following a W3C standard (well, mostly...). The original HTML is from then on completely redundant. The browser doesn't care what the original HTML structure was; its understanding of the web page is the DOM structure that was created from it. If your HTML markup was incorrect/invalid, it will be corrected in some way by the web browser; the DOM structure will not contain the invalid code in any way.

Basically, HTML should be treated as a way of serialising a DOM structure to be passed over the internet or stored in a file locally.

It should not, therefore, be used for modifying an existing web page. The DOM (Document Object Model) has a system for changing the content of a page. This is based on the relationship of nodes, not on the HTML serialisation. So when you add an li to a ul, you have these two options (assuming ul is the list element):

// option 1: innerHTML ul.innerHTML += '<li>foobar</li>';  // option 2: DOM manipulation var li = document.createElement('li'); li.appendChild(document.createTextNode('foobar')); ul.appendChild(li); 

Now, the first option looks a lot simpler, but this is only because the browser has abstracted a lot away for you: internally, the browser has to convert the element's children to a string, then append some content, then convert the string back to a DOM structure. The second option corresponds to the browser's native understanding of what's going on.

The second major consideration is to think about the limitations of HTML. When you think about a webpage, not everything relevant to the element can be serialised to HTML. For instance, event handlers bound with x.onclick = function(); or x.addEventListener(...) won't be replicated in innerHTML, so they won't be copied across. So the new elements in y won't have the event listeners. This probably isn't what you want.

So the way around this is to work with the native DOM methods:

for (var i = 0; i < x.childNodes.length; i++) {     y.appendChild(x.childNodes[i].cloneNode(true)); } 

Reading the MDN documentation will probably help to understand this way of doing things:

  • appendChild
  • cloneNode
  • childNodes

Now the problem with this (as with option 2 in the code example above) is that it is very verbose, far longer than the innerHTML option would be. This is when you appreciate having a JavaScript library that does this kind of thing for you. For example, in jQuery:

$('#y').html($('#x').clone(true, true).contents()); 

This is a lot more explicit about what you want to happen. As well as having various performance benefits and preserving event handlers, for example, it also helps you to understand what your code is doing. This is good for your soul as a JavaScript programmer and makes bizarre errors significantly less likely!

like image 169
lonesomeday Avatar answered Oct 21 '22 08:10

lonesomeday


  1. You can duplicate IDs which need to be unique.
  2. jQuery's clone method call like, $(element).clone(true); will clone data and event listeners, but ID's will still also be cloned. So to avoid duplicate IDs, don't use IDs for items that need to be cloned.
like image 29
Joe Avatar answered Oct 21 '22 08:10

Joe