picostitch
crafting (and) JavaScript

el.innerText vs. el.textContent

The innerText and textContent properties of DOM elements sound similar, but they have distinct behaviors. This post explains the differences and when to use each.

Prelude

The main topic here is: How do I get the inner content of a DOM node?
Multiple ways one may know:

  • el.innerHTML is what I remember to have learned first
  • el.innerText I think I learned second
  • el.textContent feels newest to me.

These are all the most common ways.

Let's dive into the difference of innerText and textContent, since these two get you the text in there, and they do already strip out the HTML, which is what is often needed when one wants to work with the actual content.

The Basics

Reading el.innerText or el.textContent returns the content of DOM node referred to by el. For example, let's assume we have the following HTML:

<article id="my-article" style="color: blue;">the article's inner text</article>
<script>
  const el = document.querySelector('#my-article');
  document.write('innerText: ' + el.innerText + '<br>'); // write "the article's inner text" into the page
  document.write('textContent: ' + el.textContent); // same as above
</script>

Proof: executing the above code results in:

the article's inner text

The blue text is the actual <article> (check it via right-mouse-click "Inspect" if you like). The document.writes output is right below.
NOTE: document.write is a "very old" function that is actually discouraged to use. It writes straight into the page in place, this is only possible because the <script> tag is render-blocking.

We set the basics, now let's dive a bit deeper.

Embedded HTML

Using el we can refer to any DOM node, which can also have children, cool thing is both remove the HTML and just return the text. While having the DOM nodes a <b>c</b> or a c reading their content will return the same thing.

What if the tag <b> has some style applied to it?

For example a <b style="display:none">c</b> will still return a c, but ONLY for el.textContent. innerText returns the visible text only, which will be just a!

See the following:

<article>
  with <span style="display: none">an invisible span inside</span> or not?
</article>

It renders this:

with an invisible span inside or not?

Notice that textContent also contains the invisible "an invisible span inside" string from inside the <span>, while innerText does not.

The Surrounding HTML

The visibility is also influenced by surrounding elements. It is quite obvious that an <article> nested in an <main style="display: none"> is not shown and has no innerText. But there are more subtle behaviors that have an impact.
Embed the <article> in a <details> node, that the user can toggle open.

Let's try it:

<details>
    <summary>Open/close me</summary>
    <article>Inside of a details node</article>
</details>

This renders:

Open/close me
Inside of a details node

Ooops, do you see that the <details> element is actually opened? That is because I added the code document.getElementById('3rd-details').open = true; to open it after the innerText and textContent was written for the first time. The result afterward is different because the <article> is visible now.

The HTML/CSS specifications say that the "computed value of 'visibility' is 'visible'" then the content is returned in innerText. Important to note is that "computed value" is not necessarily the "computed style", the CSS spec says. I was not able to find out a way to get the "computed value" via JavaScript. If sounds abstract? I agree. Anyway.

The browser is pretty smart about what "visible" means! Or not?

Overlayed Content

What about content that is overlayed by other content? For example a absolute positioned element, which is in the background and another one is just right on top of it.

Let's try it:

<article id="my-4th-article" style="position: absolute; left: 1em;">
  Absolute positioned
</article>
<article style="background: white; opacity: 0.8; position: relative;">
  O v e r l a y e d  content
</article>

The overlay element has an explicit white background, a bit of opacity, just to prove that there is another one behind it.

It all renders like this:

Absolute positioned
O v e r l a y e d content

So the browser is not that smart. The overlaying text is not returned even though that is the text we see in the place of the actual <article>.
But to be honest, if that had worked the spec would probably be very much more complicated. Unnecessarily for such an edge case, I would say.

Overflowing Content

What about content that is cut off due to width limitations and hidden overflows? I expect it to work just as above, it is just way too edgy to detect that.

Let's see:

<span style="display: inline-flex; width: 4em; white-space: nowrap; overflow: hidden; background: lightyellow;">
  Only 4em wide!
</span>
<span>Content after</span>

The first <span> renders only with 4em width, the rest is cut off. The next <span> starts right after it. That could mean the innerText is also cut off, but I would be surprised.

Only 4em wide! Content after

As expected the innerText contains the entire text even though it is visually cut off.

Off Screen Content

What about off-screen DOM elements? By this I mean an document.createElemented DOM node that is not rendered on the screen, it only exists in memory. So it is actually also not visible yet.

Let's create an element, give it some innerText explicitly and then read the two properties of it.

<script>
  const el = document.createElement('article');
  el.innerText = 'I am off-screen ARTICLE';
</script>

The output is:

The MDN page also states this explicitly to work like that. Why? I guess that would need some digging into the HTML spec, feel free to do so find the links below.

Conclusion

This was a fun discovery, I learned a lot about the inner workings and also where to look for these two properties in the specification. They do not do magic, but it is useful to know the difference, especially in the case that it bites you. I experienced that, my initial motivation was triggered by a closed <details> element. Now I know. I hope you were also able to take away something.

innerText returns the visible text content of an element, taking into account CSS styles and layout but with limitations! textContent returns all the content, no matter if visible or not, HTML tags are stripped though. If you like to keep the HTML tags use innerHTML.

If you want a less verbose, maybe easier to read description visit MDN's innerText and MDN's textContent. If you like to dive deep read the HTML specification on innerText or textContent.