Why should there be y.innerHTML = x.innerHTML; to avoid?

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

 y.innerHTML = x.innerHTML; 

or using jQuery:

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

However, this method does not seem to be a good idea and should be avoided.

(1) Why should this method be avoided?

(2) How to do it instead?




Update:
For the sake of this question, let's say that there are no elements with an identifier in DIV x . (Sorry, I forgot to consider this case in my original question.)

Output:
I posted my own answer to this question below (as I expected). Now I also planned to accept my own answer :P , but the answer to lonesomeday is so amazing that I should accept it instead.

+45
javascript jquery html browser
Sep 12 '11 at 19:29
source share
6 answers

This method of "copying" HTML elements from one place to another is the result of a misunderstanding of what the browser does. Browsers do not store the HTML document in memory somewhere and repeatedly modify HTML based on commands from JavaScript.

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

In principle, HTML should be considered as a way to serialize the DOM structure, which must be transmitted over the Internet or stored in a local file.

Therefore, it should not be used to modify an existing web page. The DOM (Document Object Model) has a system for modifying page content. This is based on node relationships, not HTML serialization. Therefore, when you add li to ul , you have these two options (assuming ul is a list item):

 // 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 only happens because the browser is very distracted by you: internally, the browser must convert the children to a string, then add some content, and then convert back to the DOM structure. The second option corresponds to the concept of the browser about what is happening.

A second important consideration is to consider the limitations of HTML. When you think of a web page, not everything that is relevant to an element can be serialized in HTML. For example, event handlers associated with x.onclick = function(); or x.addEventListener(...) will not be replicated to innerHTML , so they will not be copied. Thus, new elements in y will not have event listeners. This is probably not what you want.

So, the way around this is to work with your own 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:

Now the problem with this (as with option 2 in the example above) is that it is very verbose, much longer than the innerHTML option. This is when you value having a JavaScript library that does this for you. For example, in jQuery:

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

This is a much more explicit description of what you want. For example, with various performance benefits and maintaining event handlers, it also helps to understand what your code does. This is good for your soul as a JavaScript programmer and makes fancy bugs significantly less likely!

+71
Sep 12 '11 at 19:48
source share
  • You can duplicate identifiers that must be unique.
  • jQuery clone method calls, $(element).clone(true); will clone data and event listeners, but the ID will still be cloned. Therefore, to avoid duplicate identifiers, do not use identifiers for objects that you want to clone.
+9
Sep 12 '11 at 19:31
source share
  • It should be avoided because then you lose any handlers that might be on this DOM.

  • You can try to get around this by adding clones of DOM elements rather than completely rewriting them.

+7
Sep 12 '11 at 19:32
source share

First we define the task to be performed here:

All child nodes of DIV x must be “copied” (together with all its descendants = deep copy) and “pasted” into DIV y . If any of the descendants of x has one or more event handlers associated with it, we presumably want these handlers to continue working on the copies (after they were placed inside y ).

Now this is not a trivial task. Fortunately, the jQuery library (and all the other popular libraries, I suppose) offers a convenient method to accomplish this task: .clone() . Using this method, the solution can be written like this:

 $( x ).contents().clone( true ).appendTo( y ); 

The above solution is an answer to question (2). Now let's look at question (1):

it

 y.innerHTML = x.innerHTML; 

- This is not just a bad idea - it is terrible. Let me explain ...

The above statement can be divided into two stages.

  • The expression x.innerHTML ,

  • The return value of this expression (which is a string) is assigned to y.innerHTML .

The nodes that we want to copy (child nodes of x ) are DOM nodes. These are objects that exist in the browser memory. When evaluating x.innerHTML browser serializes (builds) these DOM nodes into a string (HTML source code string).

Now, if we need such a string (for example, to store it in a database), this serialization would be understandable. However, we do not need such a string (at least not as a final product).

In step 2, we assign this line to y.innerHTML . The browser evaluates this by parsing the string, which results in a set of DOM nodes, which are then inserted into the DIV y (as child nodes).

So to summarize:

child nodes xline → HTML source code line → parsing → nodes (copies)

So what is the problem with this approach? Well, DOM nodes can contain properties and functionality that they cannot and therefore will not be serialized . The most important of these features are event handlers associated with the descendants of x — copies of these elements will not be associated with event handlers. Handlers got lost in the process.

An interesting analogy can be made here:

Digital Signal → D / A Conversion → Analog Signal → A / D Conversion → Digital Signal

As you probably know, the resulting digital signal is not an exact copy of the original digital signal - some information was lost in the process.

I hope you now understand why you should avoid y.innerHTML = x.innerHTML .

+7
Sep 12 2018-11-11T00:
source share

I would not do this simply because you are asking the browser to re-smooth the HTML markup that has already been parsed.

I would be more likely to use the native cloneNode(true) to duplicate existing DOM elements.

 var node, i=0; while( node = x.childNodes[ i++ ] ) { y.appendChild( node.cloneNode( true ) ); } 
+4
Sep 12 '11 at 19:41
source share

Well, it really depends. There is the possibility of creating duplicate elements with the same identifier, which is never good.

jQuery also has methods that can do this for you.

+2
Sep 12 '11 at 19:32
source share



All Articles