【TA不知道的CSS】white-space

2,072 阅读6分钟

我有一个朋友叫小帅,是位前端开发工程师。

这天,小帅在开发一个文本编辑功能时遇到了点小问题。

文本编辑的功能长这样:

image.png

保存数据时的文本字符串长这样:

image.png

可是最后的结果却是这样:

image.png

小帅同学思考了两秒钟,立马想了一个绝妙的解决办法,在展示的时候将字符串中的\n替换成<br>标签,然后使用rawHtml的方式展示不就好了嘛~

此时,旁边的小美悠悠地来了一句:难道你不知道CSS有个属性叫white-space吗❓


属性描述

我们从官方定义及具体场景两个方面来看看white-space的在这里的作用。

官方定义

W3C官方描述中,white-space主要有以下两个作用:

  • 是否进行空格合并,以及控制空格合并的方式;
  • 是否在soft wrap opportunities(文本中可进行换行的断点位置)处进行文本换行。

从字面意思来看white-space即为空格,因此对于其第一个作用改变空格的表现形式大家都很容易理解,那为什么这个属性也会影响换行呢?

这里还是得回到soft wrap opportunities的定义。

In most writing systems, in the absence of hyphenation a soft wrap opportunity occurs only at word boundaries. Many such systems use spaces or punctuation to explicitly separate words, and soft wrap opportunities can be identified by these characters. Scripts such as Thai, Lao, and Khmer, however, do not use spaces or punctuation to separate words. Although the zero width space (U+200B) can be used as an explicit word delimiter in these scripts, this practice is not common. As a result, a lexical resource is needed to correctly identify soft wrap opportunities in such texts.

In some other writing systems, soft wrap opportunities are based on orthographic syllable boundaries, not word boundaries. Some of these systems, such as Javanese and Balinese, are similar to Thai and Lao in that they require analysis of the text to find breaking opportunities. In others such as Chinese (as well as Japanese, Yi, and sometimes also Korean), each syllable tends to correspond to a single typographic letter unit, and thus line breaking conventions allow the line to break anywhere except between certain character combinations. Additionally the level of strictness in these restrictions varies with the typesetting style.

官方的定义很长,我知道你们也懒得看,所以这里简单总结一下,当文本长度超出容器时,浏览器在单词边界、连字符、音节、标点符号、空格等地方都可以进行换行,这些点即是soft wrap opportunities

所以答案明了,空格是可以进行换行的地方,而white-space用来影响空格的表现,故white-space的第二个作用便是影响文本换行。

这里也解释了为什么white-spaceword-breakword-wrap等多个属性都可以改变文本的换行行为,它们改变的都是soft wrap opportunities,间接导致了换行的变化。

场景还原

我们回到我的这个朋友的问题场景中,上面的定义也只是说了white-space空格的关系,即使white-space能影响换行,也应该是在有空格的情况下,这里跟换行符\n有什么关系呢?

这就涉及到另一个重点了,在浏览器渲染页面时,会把所有的换行符都渲染成空格!

浏览器渲染页面的根本是将HTML渲染成可见的内容,而在HTML中只有<br>标签用于换行,其他的换行符如\n\r\n等只有在支持解析的语法中才会显示,如JavaScriptconsole

在浏览器的渲染过程中,DOM解析完成后将与CSSOM进行合并,此时DOM树中文本里的换行符\n已经被转换成了空格,当发现CSSOM中对应属性有white-space时,该属性值发生作用,最终将渲染后的结果展示在页面中。

这里可以用下面的图示来解释整体的过程:

image.png

属性值

在弄清原理之后,我们在MDN的基础上,详细学习一下white-space的几个主要属性值。

normal

连续的空白符会被合并,换行符会被当作空白符来处理。换行在填充「行框盒子 (line boxes)」时是必要。

该属性值为浏览器的默认样式,会将文本中的换行符转换为空白符,并且连续的空白符会进行合并。这里的「行框盒子」可以简单理解为当文本长度超过盒子宽度时会进行换行操作。

nowrap

和 normal 一样,连续的空白符会被合并。但文本内的换行无效。

该属性值用于控制文本不换行,但需要注意的是这里「文本内的换行」主要是指换行符\n,而换行标签<br>还是能够使文本换行的。

pre

连续的空白符会被保留。在遇到换行符或者<br>元素时才会换行。

该属性值会保留文本中连续的空白符,不进行合并操作,如果文本中没有换行符或者<br>标签则不会进行换行操作。

pre-wrap

连续的空白符会被保留。在遇到换行符或者<br>元素,或者需要为了填充「行框盒子 (line boxes)」时才会换行。

该属性值相比于pre会始终对文本进行换行操作。

pre-line

连续的空白符会被合并。在遇到换行符或者<br>元素,或者需要为了填充「行框盒子 (line boxes)」时会换行。

该属性值相比于pre-wrap会将连续的空白符进行合并。

break-spaces

与 pre-wrap的行为相同,除了:

  • 任何保留的空白序列总是占用空间,包括在行尾。
  • 每个保留的空格字符后都存在换行机会,包括空格字符之间。
  • 这样保留的空间占用空间而不会挂起,从而影响盒子的固有尺寸(最小内容大小和最大内容大小)。

这是一个很少见的属性值,在MDN中也没有很明确的示例。根据上面的描述,我们可以知道该属性值与pre-wrap相比差别主要是在文本行尾的空格。

pre-wrap中文本行尾空格较多时,虽然会进行保留,但并不会进行换行操作,多余的空格会超出盒子。 break-spaces中文本行尾空格则会进行换行操作。

额外需要注意的是该属性值存在兼容性问题,在 Internet Explorer 和 Firefox for Android 中无法生效

总结

根据上述6个属性值的特性,我们可以将其总结为以下表格内容。

换行符空格和制表符文字换行行尾空格
normal合并合并换行删除
nowrap合并合并不换行删除
pre保留保留不换行保留
pre-wrap保留保留换行挂起
pre-line保留合并换行删除
break-spaces保留保留换行换行

经过小美的一番教导,小帅终于明白了,原来只需要添加一个white-space: pre-wrap属性就可以完美的解决问题。

“真好呀,今天又学习了。”