Div innerText 在将显示设置为 none 后丢失新行

Div innerText loses new lines after setting display to none

在编写基于自定义所见即所得 HTML 的编辑器时,我遇到了这种奇怪的行为。

jsfiddle

在 div 中输入一些包含新行的文本,contentEditable 设置为 true

此时 div.innerText 完全包含输入的文本,包括新行。

然后设置div.style.display = none并重新检查div.innerText:它是相同的文本,但是新行被删除了。

这是为什么?这种情况有“标准行为”吗?

(在 FF Developer Edition 89.0b3(64 位)和 Chrome 版本 90.0.4430.85(官方构建)(64 位)中测试)

=>跟进 还有一个类似的奇怪问题:

var d = document.getElementById("divTest")

function setup() {
    d.innerText = "1\n2"
}

function log() {
    console.log(d.innerText)
  logChildren()
}

function logChildren() {
    console.log("Child nodes: ");
    d.childNodes.forEach(node => {
    console.log(node.toString());
  });
}
div[contenteditable] {
  border: 1px solid red;
}
<div contenteditable="true" id="divTest"></div>
<input type="button" value="setContent" onclick="setup()"/>
<input type="button" value="log" onclick="log()"/>

单击 setContent 按钮,然后单击 log 按钮。输出符合预期:

1
2

[object Text]
[object HTMLBRElement]
[object Text]

然后在输入里面点击div。在 2 后按 enter 转到新行,然后按 3。一个得到

1
2
3

在 div 中,人们希望在 div.innerText 中得到相同的结果,但不幸的是:

1

2
3
Child nodes: 
[object Text]
[object HTMLBRElement]
[object HTMLDivElement]
[object HTMLDivElement]

为什么 1 是 [object Text] 而 2 和 3 [object HTMLDivElement]?为什么1和2之间会有空行?等...

对我来说没有任何意义。

innerText 的实现取决于元素是否可见。正如@Nisala 在评论中指出的那样,如果将 divdisplay 属性设置回 blockinnerText 将再次包含换行符。

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>


如果我们看一下 innerText documentation,我们会看到 getter 行为的第一步定义如下:

  1. 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.

所以当我们的 div 没有被渲染时,我们应该期望 innerText returns 我们的 divtextContent。的确,这就是我们所看到的。

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>


那么,当 div 可见时,为什么我们的 innerText 中会出现换行符?文档继续:

  1. Let results be a new empty list

  2. For each child node node of this:

  1. 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).

在这种情况下,innerText 将忽略 textContent,而是对 childNodes 列表进行操作。让我们看看它对我们的 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>


如您所见,按 ENTER 键通过 添加一个 div 来为我们的 div 的内容添加一个换行符div 的 childNodes 列表。为什么会出现这种情况超出了这个问题的范围,但它本身就是一个很好的问题。

如果您正在使用页内编辑器,HTML 规范 has a section containing best practices


回顾一下:

如果div可见,则innerTextgetter使用divtextContent属性。

如果 div 不可见,则 childNodes 树中的每个节点都遵循 inner text collection steps,并将结果连接在一起。

在为我们的 div 计算 innerText 的值时,display 属性的值很重要,因为它决定 textContent 属性 或将使用 childNodes 树的评估。


注意:@Domino 在 中提供了更多信息。