innerHTML in JavaScript: replacing the HTML content of an element

innerHTML reads or replaces the HTML content of an element: what writing to it destroys on the way, the injection risk, and the safer options.
3 min read
Believemy logo

Filling an area of the page with several elements sometimes fits on one line. That is the promise of innerHTML, and the reason for its popularity.

It is also the property causing the most silent damage, from the field emptied for no reason to the security hole opened without anyone noticing.


Definition

innerHTML stands for the HTML content of an element, as a string. Read from, it hands back the markup of the children. Written to, the browser parses the string given, builds nodes from it, and replaces all the existing content.

JAVASCRIPT
const list = document.querySelector("#cart");

list.innerHTML = "<li>Keyboard</li><li>Mouse</li>";
console.log(list.children.length);   // 2
console.log(list.innerHTML);

The difference with textContent is essential: the latter treats the string as plain text, so the tags then show up on screen exactly as written.


What writing destroys

The assignment does not edit the existing nodes, it throws them away and builds new ones. Everything attached to the old ones disappears with them.

  • Listeners placed by addEventListener() on the children are lost.
  • Form fields go back to their original value, including text already typed in.
  • Focus and scroll position are reset.

Writing element.innerHTML += "..." makes it worse: it reads all the content back, concatenates, then rebuilds the whole thing. Over a loop of a hundred items, the work is redone a hundred times.


The injection risk

Inserting a string that came from a visitor amounts to letting that person write HTML into the page. This is the flaw known as script injection, and it allows a session token to be read or data to be sent elsewhere.

JAVASCRIPT
const message = '<img src="x" onerror="alert(1)">';

zone.innerHTML = message;      // the code runs
zone.textContent = message;    // the text shows up, harmless
Good to know

A <script> tag inserted through innerHTML does not run, that much is true. It is no protection though: event attributes such as onerror or onload work perfectly well.


The alternatives

SpellingWhen to use it
textContentAs soon as text is involved, so most of the time
insertAdjacentHTMLAdding markup without rebuilding what is there
createElementBuilding elements whose reference you want to keep


Frequently asked questions

Question

Is it faster than creating elements one by one?

For one large insertion in a single pass, parsing a string is often competitive. The advantage flips as soon as the property is rewritten in a loop, and createElement() keeps a reference on each element, which saves searching for it afterwards.


Question

How do you add without rebuilding everything?

insertAdjacentHTML takes a position and a string, and inserts without touching the neighboring nodes. The four accepted positions are beforebegin, afterbegin, beforeend and afterend, the last one placing the content right after the element.


Question

How can HTML be inserted safely?

The rule fits in one sentence: only insert markup you wrote yourself, never a string coming from a form or an interface. Interface libraries settle the matter differently, React escaping text by default and demanding a deliberately off-putting property name to insert raw HTML, a mechanism covered in the React course.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.