3.2 深入了解CSS逻辑属性
整个CSS世界就是围绕“流”来构建的(详见《CSS世界》一书的第6页,1.3.2节)。在CSS2.1时代,CSS属性的定位都是基于方向的,而不是“流”。这样的设计其实是有问题的,基于方向进行定位虽然符合现实世界认知,但和CSS世界基于“流”的底层设计理念不符,这样就会产生不合理的问题。
举个例子,有两个按钮,HTML代码如下:
<p> <button>确定</button><button>取消</button> </p>
这两个按钮是左对齐按钮,因为彼此之间需要一点间距,所以就设置了下面这样的CSS:
button { margin-right: 10px; }
其中,margin-right就是一个基于方向的CSS属性,因为right表示右侧,和现实世界的右侧匹配。
通常情况下,我们这么使用是不会有任何问题的。但是,如果开发者使用direction属性改变了文档的水平流向,希望按钮从右往左排列,这段CSS声明就会有预期之外的表现,示例如下:
p { background-color: deepskyblue; direction: rtl; }
此时margin-right产生的10px间隙就不是我们想要的,如图3-13所示。margin-left:10px才是我们想要的。
图3-13 margin-right在文档流方向改变后的问题示意
但是,如果我们一开始设置的不是符合现实世界认知的margin-right属性,而是符合CSS世界“流”概念的逻辑属性margin-inline-end,代码如下:
button { margin-inline-end: 10px; }
那么我们使用direction属性改变文档的水平流向是不会出现布局上的任何问题的,如图3-14所示。
图3-14 使用margin-inline-end后的布局效果示意
margin-inline-end是一个“流淌”在文档流中的CSS逻辑属性,表示内联元素文档流结束的方向。也就是说,当文档流的方向是从左往右的时候,margin-inline- end属性的渲染表现就等同于margin-right属性;当文档流的方向是从右往左的时候,margin-inline-end属性的渲染表现就等同于margin-left属性。
上面的例子有对应的演示页面,读者可以在浏览器中进入https://demo. cssworld.cn/new/3/2-1.php页面,或者扫描右侧的二维码查看效果。
3.2.1 CSS逻辑属性有限的使用场景
CSS逻辑属性需要配合writing-mode属性、direction属性或者text-orientation属性使用才有意义。
CSS中还有其他一些CSS属性值也可以改变DOM元素的呈现方向,例如flex-direction属性中的属性值row-reverse和column-reverse,但是请注意,这些属性值和CSS逻辑属性之间没有任何关系。例如:
<div class="flex"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> </div> .flex { display: flex; flex-direction: row-reverse; } .item { flex: 1; padding: 40px; border-inline-start: 1rem solid deepskyblue; background-color: azure; }
结果如图3-15所示,虽然.item从右往左呈现,但是border-inline-start属性表示的依然是左边框。
图3-15 row-reverse和CSS逻辑属性没有关联示意
上面的例子有对应的演示页面,读者可以在浏览器中进入https://demo. cssworld.cn/new/3/2-2.php页面,或者扫描右侧的二维码查看效果。
writing-mode、direction和text-orientation属性都不是常用CSS属性,这就导致CSS逻辑属性的使用场景非常有限。有些人可能会说,平常使用margin-inline-end属性代替margin-right属性不就好了?对,但是这样做没有必要,因为margin-right属性兼容性更好,且更容易理解,再怎么考虑也不会想到使用margin-inline-end属性代替。
当然,也存在非常适合使用CSS逻辑属性的场景,那就是对称布局,例如,图3-16所示的模拟微信对话的效果就是典型的对称布局。
这种布局效果使用CSS逻辑属性实现会有较好的体验,因为我们只需要使用CSS逻辑属性实现一侧的布局效果,然后另外一侧的布局效果我们只需要使用一句direction:rtl就完成了,代码超级简洁:
<section> <!-- 其他HTML,略…… -->[3] </section> <section data-self> <!-- 自己对话内容,和上面HTML一样,略…… --> </section> [data-self] { direction: rtl; }
图3-16 模拟微信对话的对称布局效果示意
如果对具体的实现细节感兴趣,读者可以在浏览器中进入https://demo. cssworld.cn/new/3/2-3.php页面,或者扫描右侧的二维码查看效果。
那么问题来了,既然CSS逻辑属性使用场景比较有限,那我们还要不要学呢?当然要学,因为学习它的成本实在是太低了,所以投入产出比其实还不错。
3.2.2 inline/block与start/end元素
只要理解了本节的inline/block与start/end,CSS逻辑属性就算学完了,因为所有CSS逻辑属性都是围绕inline/block与start/end展开的。
以margin属性为例,在中文或英文网页环境中,默认情况下,margin方位属性和margin逻辑属性相互的映射关系如下:
margin-left ↔ margin-inline-start margin-top ↔ margin-block-start margin-right ↔ margin-inline-end margin-bottom ↔ margin-block-end
其中,inline/block表示方向,start/end表示起止方位。
在中文和英文网页环境中,inline元素(文字、图片、按钮等)默认是从左往右水平排列的;block元素(如<div>、<p>元素等)默认是从上往下垂直排列的。因此,margin-inline-start就表示内联元素排列方向的起始位置,即“左侧”;margin-inline-end就表示内联元素排列方向的终止位置,即“右侧”。
如果设置direction:rtl,则水平文档流方向就是从右往左,此时start对应的就是“右侧”,end对应的就是“左侧”。如果设置writing-mode:vertical-rl属性,把文档流改为垂直且从右往左排列,则此时内联元素是从上往下排列的。inline指的是垂直方向,block指的是水平方向,margin方位属性和margin逻辑属性相互映射关系就变成了下面这样:
/* writing-mode:vertical-rl环境下 */ margin-left ↔ margin-block-end margin-top ↔ margin-inline-start margin-right ↔ margin-block-start margin-bottom ↔ margin-inline-end
如果对上面的映射关系存疑,可以在浏览器中进入https://demo.cssworld.cn/ new/3/2-4.php页面,或者扫描右侧的二维码进行确认。
下面快速介绍一下你可能会用到的CSS逻辑属性和CSS逻辑属性值。
3.2.3 width/height属性与inline-size/block-size逻辑属性
在中文或英文网页环境中,默认情况下,width属性对应的CSS逻辑属性是inline-size,height属性对应的CSS逻辑属性是block-size。
width属性新支持的几个关键字属性值也可以作为inline-size的属性值,例如:
/*浏览器支持 */ inline-size: fit-content; inline-size: min-content; inline-size: max-content;
除了width属性和height属性,min-width、min-height、max-width和max-height也都有对应的CSS逻辑属性,示例如下:
● min-inline-size;
● min-block-size;
● max-inline-size;
● max-block-size。
兼容性
inline-size和block-size属性的兼容性如表3-1所示。
表3-1 inline-size和block-size属性的兼容性(数据源自MDN网站)
IE浏览器和Edge浏览器并不支持这两个属性,而移动端目前全部支持这两个属性,很快就可以放心使用。接下来要介绍的几个CSS逻辑属性的兼容性也是类似的。
3.2.4 由margin/padding/border演变而来的逻辑属性
margin和padding属性对应的CSS逻辑属性很早就被支持了,最早可以追溯到2008年,然而当时使用的不是现在的语法,且只支持水平方向上的逻辑控制,同时需要添加私有前缀,如下所示:
● -webkit-margin-start、-webkit-margin-end;
● -webkit-padding-start、-webkit-padding-end。
在规范稳定之后,margin、padding和border属性一起,演变成了按照inline/block与start/end这几个关键字组合的新的CSS逻辑属性,无须私有前缀。新的CSS逻辑属性如下:
● margin-inline-start、margin-inline-end、margin-block-start、margin- block-end;
● padding-inline-start、padding-inline-end、padding-block-start、padding-block-end;
● border-inline-start、border-inline-end、border-block-start、border- block-end;
● border-inline-start-color、border-inline-end-color、border-block- start-color、border-block-end-color;
● border-inline-start-style、border-inline-end-style、border-block- start-style、border-block-end-style;
● border-inline-start-width、border-inline-end-width、border-block- start-width、border-block-end-width。
现在还支持CSS逻辑属性的缩写语法,例如margin-inline属性是margin-inline- start属性和margin-inline-end属性的缩写,margin-block属性是margin-block- start属性和margin-block-end属性的缩写。完整的CSS缩写逻辑属性如下:
● margin-inline、margin-block;
● padding-inline、padding-block;
● border-inline、border-block;
● border-inline-color、border-block-color;
● border-inline-style、border-block-style;
● border-inline-width、border-block-width。
可以看到CSS缩写逻辑属性的数量是非常多的,但是,它们都是由传统的带有方位性质的CSS属性按照特定规则演变而来的,即把原来的left、top、right、bottom换成对应的inline/block与start/end并组合。
兼容性
margin/padding/border相关的CSS逻辑属性的兼容性如表3-2所示。
表3-2 margin/padding/border相关的CSS逻辑属性的兼容性(数据源自MDN网站)
由于目前Safari浏览器并不支持CSS缩写逻辑属性,因此要慎用CSS缩写逻辑属性,最好使用包含start和end的基本CSS逻辑属性。
3.2.5 text-align属性支持的逻辑属性值
对text-align属性而言,演变的不是属性而是属性值。
● text-align: start。
● text-align: end。
兼容性
text-align支持start和end属性值的兼容性如表3-3所示。
表3-3 text-align支持start和end属性值的兼容性(数据源自MDN网站)
3.2.6 最有用的CSS逻辑属性inset
使用绝对定位的时候经常会用到left、top、right、bottom等属性。同样,在CSS新世界中也有与之相对应的CSS逻辑属性,全部都是以inset开头,这其中包括:
● inset-inline-start;
● inset-inline-end;
● inset-block-start;
● inset-block-end。
也包括水平方位或者垂直方位的缩写:
● inset-inline;
● inset-inline;
● inset-block;
● inset-block。
还包括完整的缩写:
● inset。
以上属性中最有用的当属inset属性,在使用绝对定位或固定定位的时候,我们经常会使用下面的CSS代码:
.overlay { position: absolute; left: 0; top: 0; right: 0; bottom: 0; }
有了inset属性,事情就简单多了:
.overlay { position: absolute; inset: 0; }
这就很有意思了,CSS大多数的逻辑属性在平时开发过程中都用不到,只有一个例外,那就是inset属性。inset属性有两大特点,一个是逻辑,另一个是缩写。当然,逻辑就是摆设,平常根本用不到。但是缩写实在是太诱人了,因为绝对定位元素在4个方向上同时定位是很常见的,每次都要写4个属性真的很麻烦。inset属性谁用了都说好,以后一定会成为热门的CSS属性。
目前阻碍inset属性普及的唯一因素就是兼容性,主要问题出在Safari浏览器。在我写本书的时候,Safari浏览器不支持所有缩写逻辑属性,希望本书出版后能看到Safari浏览器支持这些缩写逻辑属性的好消息。最后提一句,inset属性支持的值的数量范围是1~4,例如:
inset: 100px; inset: 100px 200px; inset: 100px 200px 300px; inset: 100px 200px 300px 400px;
不同数量的值所表示的方位和margin、padding等属性一样,这里不再赘述。
虽然前面几节列举了很多CSS逻辑属性,但是这不是全部,还有很多其他CSS逻辑属性或者CSS逻辑属性值,如scroll-margin、scroll-padding,以及它们衍生出的十几个CSS逻辑属性,以及float属性和clear属性支持的inline-start和inline-end逻辑属性值等。CSS逻辑属性大同小异,这里就不一一展开说明了。