在编写基于所见即所得HTML的自定义编辑器时,我遇到了这种奇怪的行为。
jsfiddle
在div中输入一些包含新行的文本,其中contentEditable
设置为true
。
此时,div.innerText
正好包含包含新行的输入文本。
然后设置div.style.display = none
并重新检查div.innerText
:它是相同的文本,但新行被删除。
为什么?是否存在";标准行为";对于这种情况?
(在FF开发者版89.0b3(64位(和Chrome版本90.0.4430.85(官方版本((64位
=>跟进还有另一个类似的奇怪问题:
var d = document.getElementById("divTest")
function setup() {
d.innerText = "1n2"
}
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在评论中指出的那样,如果将div
的display
属性设置回block
,则innerText
将再次包含换行符。
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
文档,我们会看到getter行为的第一步定义如下:
- 如果没有呈现,或者如果用户代理是非CSS用户代理,则返回其子代文本内容
注意:此步骤可能会产生令人惊讶的结果,因为当对未渲染的元素调用
innerText
getter时,会返回其文本内容,但当对正在渲染的元素进行访问时,会忽略其所有未渲染的子元素的文本内容。
因此,当我们的div
没有被渲染时,我们应该期望innerText
返回div
的textContent
。事实上,这就是我们所看到的。
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
中会出现换行符?文件继续:
让结果成为新的空列表
对于的每个子节点节点
- 让current是导致使用节点运行内部文本收集步骤的列表。结果中的每个项都将是字符串或正整数(需要换行计数(
在这种情况下,
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
的childNodes列表,将新行添加到div的内容中。为什么会出现这种情况超出了这个问题的范围,但就其本身而言,这将是一个很好的问题。如果您正在使用页面内编辑器,HTML规范中有一部分包含最佳实践。
回顾:
如果
div
可见,则innerText
getter使用div
的textContent
属性。如果
div
不可见,则对childNodes
树中的每个节点执行内部文本收集步骤,并将结果连接在一起。当为我们的
div
计算innerText
的值时,display
属性的值很重要,因为它决定了是使用textContent
属性还是使用childNodes
树的评估。
注意:@Domino的回答中有更多信息。