ComparisonComparisons

innerHTML vs textContent

innerHTML reads and sets an element’s HTML markup, parsing the string into new elements, while textContent reads and sets plain text, with no markup.

On this page

Side-by-side comparison

#

Example

#
JavaScript
const box = document.querySelector('#box')

console.log(box.innerHTML)
console.log(box.textContent)

box.textContent = 'All tasks done'
console.log(box.innerHTML)
Console
Important: 2 tasks left
Important: 2 tasks left
All tasks done

Definition and usage

#

Both properties read and overwrite an element’s contents, and assigning a new value always removes the existing children. What sets them apart is how they treat the string. innerHTML returns the HTML markup as text and parses the assigned string as HTML, turning it into real elements. textContent returns just the text of the element and its descendants, and inserts the assigned string literally, as a single text node.

That difference brings a risk with it. Inserting a string from a user or an API through innerHTML opens the door to an XSS attack, because the characters < and > turn into working markup. textContent is safe: the same characters show up on screen as text. innerHTML is also slower, because it requires parsing HTML, and it destroys event handlers attached to the old elements on every assignment.

Use textContent for displaying text, especially data from a user. Leave innerHTML for inserting larger chunks of HTML you know up front, for example a list built with a template literal, remembering to sanitize every value that comes from a user.

Syntax

#
Syntax
const html = element.innerHTML
element.innerHTML = htmlString

const text = element.textContent
element.textContent = newText

More examples

#
innerHTML += destroys old elements
JavaScript
const list = document.querySelector('#list')
const firstItem = document.querySelector('#first')

list.innerHTML += '<li>Next step</li>'

console.log(document.querySelector('#first') === firstItem)
console.log(firstItem.isConnected)
Console
false
false

innerHTML += reads the whole HTML markup, glues on the new fragment, and parses everything again from scratch. The new #first element looks the same, but it’s a different object, and the old one is detached from the document.

textContent shows markup as text
JavaScript
const comment = document.querySelector('#comment')

comment.textContent = 'Use <strong> to make text bold'

console.log(comment.children.length)
console.log(comment.textContent)
Console
0
Use <strong> to make text bold

The string lands in the element literally, as a single text node. The characters < and > don’t create a tag, so children.length stays 0.

Good practices

#
  • Always use textContent to display text from a user. innerHTML with unsanitized data is an open door to an XSS attack.
  • Avoid innerHTML += in a loop: every step rebuilds the entire content from scratch. Build the whole string first, or use insertAdjacentHTML().
  • To clear an element, element.textContent = "" is enough, with no risk from HTML parsing.

Related entries

#

Spotted a mistake or missing an example? Write to us.