The implementation of innerText
depends on whether or not the element is visible. As @Nisala noted in the comments, if you set the display
attribute of the div
back to block
, the innerText
contains your newline characters again.
const input = document.querySelector("#input");
function performExperiment() {
console.log(`innerText before style change: ${input.innerText}`);
input.style.display = "none";
console.log(`innerText after first style change: ${input.innerText}`);
input.style.display = "block";
console.log(`innerText after second style change: ${input.innerText}`);
}
#input {
border: 1px solid black;
}
<p>Enter multiple lines of text into the box below, then click the button</p>
<div id="input" contenteditable="true"></div>
<button onclick="performExperiment()">Experiment</button>
If we have a look at the innerText
documentation, we see the first step of the behavior for the getter is defined as follows:
- If this is not being rendered or if the user agent is a non-CSS user agent, then return this's descendant text content.
Note: This step can produce suprising results, as when the innerText
getter is invoked on an element not being rendered, its text contents are returned, but when accessed on an element that is being rendered, all of its children that are not being rendered have their text contents ignored.
So when our div
is not being rendered, we should expect that innerText
returns the textContent
of our div
. Indeed, that is what we see.
const input = document.querySelector("#input");
function performExperiment() {
input.style.display = "none";
console.log(`innerText: ${input.innerText}`);
console.log(`textContent: ${input.textContent}`);
}
#input {
border: 1px solid black;
}
<p>Enter multiple lines of text into the box below, then click the button</p>
<div id="input" contenteditable="true"></div>
<button onclick="performExperiment()">Experiment</button>
So why are the newlines present in our innerText
when the div
is visible? The documentation continues:
Let results be a new empty list
For each child node node of this:
- Let current be the list resulting in running the inner text collection steps with node. Each item in results will either be a string or a positive integer (a required line break count).
In this case, innerText
is ignoring textContent
and is instead operating on the childNodes
list. Let's see what the value of that is for our div
:
const input = document.querySelector("#input");
function performExperiment() {
input.childNodes.forEach(node => {
console.log(node.toString());
});
}
#input {
border: 1px solid black;
}
<p>Enter multiple lines of text into the box below, then click the button</p>
<div id="input" contenteditable="true"></div>
<button onclick="performExperiment()">Experiment</button>
As you can see, pressing the ENTER
key adds a newline to the content of our div by adding a div to the childNodes list of our div
. Why this is the case is outside the scope of this question, but would make for a good question on its own.
If you're working on an in-page editor, the HTML spec has a section containing best practices.
To recap:
If the div
is visible, the innerText
getter uses the textContent
property of the div
.
If the div
is not visible, the inner text collection steps are followed for each node in the childNodes
tree and the results are concatenated together.
When computing the value of innerText
for our div
, the value of the display
attribute matters because it determines whether the textContent
property or the evaluation of the childNodes
tree will be used.
Note: There's a little more information in this answer by @Domino.