Javascript 为什么设置textContent会导致布局抖动?

Javascript 为什么设置textContent会导致布局抖动?,javascript,html,performance,dom,Javascript,Html,Performance,Dom,建议textContent优于innerText,以避免布局抖动。但它的重点是检索元素的文本;对于设置元素文本,情况正好相反——至少在下面的示例中是这样 此示例使用innerText,不会产生布局抖动: <style> #test { background-color: blue; float: right; width: 100px; height: 100%; } </style> Tes

建议
textContent
优于
innerText
,以避免布局抖动。但它的重点是检索元素的文本;对于设置元素文本,情况正好相反——至少在下面的示例中是这样

此示例使用
innerText
,不会产生布局抖动:

<style>
    #test {
        background-color: blue;
        float: right;
        width: 100px;
        height: 100%;
    }
</style>
Test test test
<div id="test"></div>
<script>
    setInterval(function() {
        document.querySelector('#test').innerText = 'innerText';
    }, 100);
</script>

#试验{
背景颜色:蓝色;
浮动:对;
宽度:100px;
身高:100%;
}
测试
setInterval(函数(){
document.querySelector(“#test”).innerText='innerText';
}, 100);

但将
innerText
替换为
textContent
,并观察其颠簸:

有人能解释这种行为吗?如何避免布局混乱,并以基于标准的方式更改元素的文本?

问题: 你说得对!就像你观察到的。设置
textContent
确实会导致抖动

以下是政府必须说的话:

textContent类型为DOMString,在DOM级别3中引入

此属性返回此节点及其子节点的文本内容。当定义为null时,将其设置为无效设置时,此节点可能具有的所有可能的子节点都将被删除,如果新字符串不为空或null,则替换为包含此属性设置为的字符串的单个文本节点。

修复 非抖动方式是获取元素的文本节点并修改它们,而不是使用
textContent
innerText
(这是非标准的)

当然,如果实际文本发生变化,则必须进行绘制以更新所看到的内容

此外,我注意到当
nodeValue
innerText
的值与前一个值不同时,整个文档布局也会被破坏,如您所见:

(全屏,在新选项卡中打开)

浏览器会破坏整个布局,因为他不知道元素的大小,因此避免整个文档破坏的诀窍是设置固定的
高度
宽度
,并将
溢出设置为
隐藏
(重要)。通过这种方式,您可以告诉浏览器,无论元素的内容是什么,它都不会退出元素边界

编辑2020年11月30日:我还需要添加
contain:strict
,明确告诉浏览器内容不会退出容器

小提琴:

和证明(再次全屏)。请注意布局更新如何仅影响图元:

我会避免
innerText
:它是非标准的,在实现上差异很大,甚至在Firefox中都没有实现。如果只是附加一个文本节点,会发生什么
var textNode=document.createTextNode(“innerText”);document.getElementById(“test”).appendChild(textNode)
答案很好。我还发现了这个Mozilla bug,它记录了一些本机优化此案例的努力:我必须补充的是,
nodeValue
的作用与
innerText
相同。如果值相同,则布局不会被破坏,但当值不同时,布局会被破坏,与
textContent
:和完全相同。当一个小组件的大小相同时,可以重新加载整个文档的布局。这似乎不再起作用了。fiddle中的示例似乎是在每次更新时计算整个页面的布局(包括结果iframe之外的区域)。您是对的@Nnnes现在显示的是“需要布局5/6的节点”,而不是“2/2”。这似乎使整个布局无效。接下来分析原因。如果我添加CSS
contain:strict
,它会再次显示“2/2”,但不会将div显示为布局根。我不知道Chrome版本之间的输出信息是否有变化,或者它是否仍然不是根层。
var test = document.getElementById("test");
var a = document.createTextNode("");
test.appendChild(a);
setInterval(function(){
    a.nodeValue = "Test test test";
},100);