Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
What problem does innerHTML solves?
I understand that innerHTML
does all the following actions:
- It makes the element we work on (or even the entire DOM tree that we work on if that element is
<body>
) to be copy-pasted into a new empty document - The new document to which the data was copied, naturally lacks any JavaScript of the previous document
- Although the data was copied into a new document, the browser will present the HTML change as if it was done in the original document
What problem would this property solve that can't be done with other methods, besides perhaps emptying the current document (why doing that?) and outputting the full HTML of an element (is it really the only way?)
About the MDN documentation:
-
Replacing the contents of an element:
innerHTML
does at least one extra thing as defuncting JavaScript so I would assume it's not a mere value replacement -
Appending HTML to an element: I assume that this would typically be done with
insertAdjacentHTML()
?
1 answer
tl;dr
According to the documentation, innerHTML
property "gets or sets the HTML or XML markup contained within the element". Basically, "that's all", but let's see it in more detail.
It makes the element we work on to be copy-pasted into a new empty document
No, the property itself doesn't copy anything (acessing element.innerHTML
simply gets its value, but it doesn't make a copy of element
). You can use its value and copy it to another element, though. And note that I said "element", not "document", because I can do this with any element in the DOM.
For example, let's suppose I have this HTML:
<div id="main">
<p>lorem ipsum <span>dolor sit</span></p>
</div>
<div id="another"><p>some text</p></div>
And this JavaScript:
const main = document.querySelector('div#main');
const another = document.querySelector('div#another');
// copy main div's innerHTML to another div
another.innerHTML = main.innerHTML;
With this, the another
div will have a copy of main
div's contents: it'll have a p
inside it (which in turn has a span
). The old content (in this case, the paragraph with "some text") is replaced by the "lorem ipsum" paragraph (and also its children - in this case, the span
"dolor sit" - as well). The result is both divs with the same contents.
Actually, when you set an element's innerHTML
, the documentation says it does the following:
- The specified value is parsed as HTML or XML (based on the document type), resulting in a
DocumentFragment
object representing the new set of DOM nodes for the new elements. - If the element whose contents are being replaced is a
<template>
element, then the<template>
element's content attribute is replaced with the newDocumentFragment
created in step 1. - For all other elements, the element's contents are replaced with the nodes in the new
DocumentFragment
.
So, when I do element.innerHTML = someHTMLText
, then someHTMLText
is parsed and a DocumentFragment
is created. This object is like a "lightweight document": it has nodes representing DOM elements, just like the document
, but it contains only the nodes that were parsed. When the DocumentFragment
is set to the element
, it replaces whatever the element had before.
In the example above, when I do another.innerHTML = main.innerHTML
, it first parses main.innerHTML
(which is a string containing the HTML), resulting in a DocumentFragment
, which is set to the another
div (and any contents this div had before are replaced by the DocumentFragment
). That's why the "some text" paragraph "disappears" and is replaced by the "lorem ipsum" one.
Therefore, using innerHTML
is a way to copy contents from one element to another. But the property itself doesn't make copies. If I set element.innerHTML = someText
and someText
doesn't come from any existing element, it's not copying anything (just me being pedantic again).
Regarding "into a new empty document", I'm not sure what you mean. If you meant this:
document.body.innerHTML = someHTMLText;
Then you're not creating "a new empty document". You're just replacing the whole contents of the document's body by the new HTML text, which means the entire page will be replaced by that HTML. This doesn't create a new document, though.
Although the data was copied into a new document, the browser will present the HTML change as if it was done in the original document
As I said, it's not a new document. It's still the same, only its contents were changed.
The new document to which the data was copied, naturally lacks any whatsoever JavaScript of the previous document
Actually, the documentation says that script
tags are also set to innerHTML
, but they're not executed (although the same docs mention some ways to circumvent this).
emptying the current document (why doing that?)
I don't know, never needed to empty the current document
.
But remind that innerHTML
can be set to any element, not only the document.body
. In this case, it's useful to make "dynamic things", such as elements changing according to some user actions (if I click somewhere, it updates some part of the page, etc).
outputting the full HTML of an element (is it really the only way?)
No, it's not the only way. But it's certainly the most straightforward one.
Other ways would be to do it manually, perhaps with this recursive function:
function getHtmlContent(el, str='') {
if (el.nodeType === Node.TEXT_NODE) {
str += el.textContent;
} else {
str += '<' + el.nodeName;
if (el.attributes) {
for (var i = 0; i < el.attributes.length; i++) {
var attr = el.attributes[i];
str += ' ' + attr.name + '="' + attr.value + '"';
}
}
str += '>';
for (var node of el.childNodes) {
str = getHtmlContent(node, str);
}
str += '</' + el.nodeName + '>';
}
return str;
}
var html = getHtmlContent(someElement);
Which is tedious and error prone (I didn't test for very complex HTML structures, not sure how reliable it is).
Another option is to serialize the element, using a XMLSerializer
:
var html = new XMLSerializer().serializeToString(el);
An important difference is that both solutions above include the elements tag, while innerHTML
doesn't. Ex: if the element corresponds to <p><span>abc</span></p>
, the paragraph's innerHTML
is <span>abc</span>
, while the solutions above also include the surrounding <p>
tags. BTW, if you want the HTML to also include the own element's tag, just use outerHTML
.
Replacing the contents of an element:
innerHTML
does some extra things so I would assume it's not a mere value replacement
Yes, it does extra things. As I previously said, setting element.innerHTML = someText
makes someText
to be fully parsed, creating a DocumentFragment
, and replacing all the element
's children by this new fragment. Which takes us to the next point:
Appending HTML to an element: I assume that this would typically be done with
insertAdjacentHTML()
?
I'd say insertAdjacentHTML
is better than simply appending to innerHTML
(although I still see a lot of code using the latter).
Appending to innerHTML
like this:
element.innerHTML += someHTMLText;
Is considered bad because it forces a full parsing of the whole contents again. Remember that the code above is equivalent to:
element.innerHTML = element.innerHTML + someHTMLText;
Which means that I'm concatenating element.innerHTML
and someHTMLText
, and this creates a new string containing all the element's contents plus the new HTML text. Then this whole text will be parsed, resulting in a DocumentFragment
, that will replace the element
's contents.
insertAdjacentHTML
would be a better solution to this case, because it parses only the new text being inserted. Hence, the code above could be changed to:
element.insertAdjacentHTML('beforeend', someHTMLText);
In this case, only someHTMLText
is parsed, and inserted after the element
's last child. This is better than parsing the whole element's contents (which is done when you append to innerHTML
).
As a final note, setting innerHTML
to the empty string is a way to remove all element's children. Also, directly setting innerHTML
to some value is an option if you want to change the element's contents to something else entirely. But if you just want to change a small part of it (such as appending something in the end, or changing some specific child element), there are lots of other methods to do it (such as append
, remove
, replaceWith
, etc).
3 comment threads