为什么Text.splitText()会影响布局?

rai*_*7ow 18 html javascript css dom

假设我们的页面中有一个段落,只有一个文本块.

<p>laborum beatae est nihil, non hic ab, deserunt repellat quas. Est molestiae ipsum minus nesciunt tempore voluptate laboriosam</p>
Run Code Online (Sandbox Code Playgroud)

在DOM方面,结构是:

HTMLParagraphElement
  Text [laborum beatae est nihil...]
Run Code Online (Sandbox Code Playgroud)

现在我们将它拆分Text.splitText()两次,分开"deserunt repellat quas.Est"片段.结构变成:

HTMLParagraphElement
  Text [laborum beatae est nihil...]
  Text [deserunt repellat quas. Est]
  Text [ molestiae ipsum minus nesciunt...]
Run Code Online (Sandbox Code Playgroud)

虽然此操作会影响DOM,但它永远不会在元素级别上更改它(Text!== Element),因此我预计不会有任何视觉上的变化.

然而,splitText()影响布局,在所有经过测试的浏览器(Chrome 60,Firefox 55,Edge 14 - 所有Windows 10操作系统)中触发重新布局和重新绘制.当我们调用时ParagraphElement.normalize(),会发生同样的情况,将Text节点的数量减少到1; 再次触发重新布局和重绘.

这有一个令人讨厌的副作用,可以在这个演示中看到.如果你检查'quas附近的话.Est',你看他们实际上改变了位置!

它在Firefox中清晰可见,并且在Chrome中更加微妙(但也可以区分).令我高兴的是,Edge中没有出现过这样的"单词舞".

这个重要的原因显示在这种(一种)匀场选择引擎的演示中.这个特定的版本在Firefox中不起作用(caretRangeFromPoint暂不支持- 唉!),但即使将"point2dom"重新加入caretPositionFromPoint突出显示的文本也会重新定位 - 在Chrome中也是如此,甚至更糟.再次,它似乎在Edge中运行良好.

所以,事实上,我最感兴趣的是理解原因并找到解决方法.

这是动画gif显示第一个演示如何在Chrome中播放(我只是触发间隔点击)

当这些话进入......

颤抖在这里很微妙,但仍然可以在所有的单词上观察到.我尤其是不解,为什么imolestiae震动,因为周围的字母似乎停留在何处.

使用不太常见的字体和更多文本会变得更糟(更糟糕),例如在选择演示中.

切换到font-family:monospace没有解决这个问题,但使它看起来更糟糕:

在此输入图像描述

切换font-kerningnone也没有帮助.

更新:问题在Blink跟踪器上注册.

Sal*_*ter 6

关于重新布局/重绘它是可以预期的,因为文本节点也是DOM节点...不是DOM元素,但浏览器确实必须重新考虑布局,即使你期望它保持不变,他们可能不得不移动.也许是因为字距调整.

现在,为什么拆分文本导致一些移动?我期望的是因为浏览器分别绘制文本部分.两个相邻字母通常具有可由取决于字母的字体被减小的空间中,采取"WA",例如,W的端部是A的开始以上,即所谓字距(THX伊斯梅尔米格尔).当单独绘制文本节点时,每个文本节点必须在下一次启动之前完成,因此它可以在这些字母之间创建更大的空间,因为它可以防止任何字距调整.

对不起,字母之间的空格确实有一个名字,但我忘了......

.one {
  background-color: #FF9999;
}

.two {
  background-color: #99FF99;
}

body {
  font-size: 40px;
}

div>span {
  border: 1px solid black;
}
Run Code Online (Sandbox Code Playgroud)
<div><span>AW</span> - in the same DOM node.</div>
<div><span><span>A</span><span>W</span></span> - in two different nodes</div>
<div><span><span class="one">A</span><span class="two">W</span></span> - in two different nodes, colored</div>
Run Code Online (Sandbox Code Playgroud)

至于如何防止这种行为,最简单的解决方案是使用等宽字体.这可能并不总是在美学上可行.Kerning是嵌入在字体文件中的信息,从这些信息中剥离字体似乎是防止闪烁的最有效方法.另外,设置为CSS属性font-kerning可能会有所帮助none.

另一种方法是在文本后面或前面添加绝对元素,以模仿将文本的一部分包围到元素中的事实,但这完全取决于最终目标.

再看看CSS-Tricks有一篇关于文本呈现的好文章,它也可以帮助进行字体字距调整.

编辑:在写这个答案时,我忽略了文本是合理的事实.虽然我的回答解释了为什么在将文本节点切割成多个文本节点时会发生某种闪烁,但这并不能解释为什么浏览器似乎在计算合理空间时会出现问题.