CSS新世界
上QQ阅读APP看书,第一时间看更新

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页面,或者扫描右侧的二维码查看效果。

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逻辑属性使用场景比较有限,那我们还要不要学呢?当然要学,因为学习它的成本实在是太低了,所以投入产出比其实还不错。

只要理解了本节的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逻辑属性值。

在中文或英文网页环境中,默认情况下,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逻辑属性的兼容性也是类似的。



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逻辑属性。



对text-align属性而言,演变的不是属性而是属性值。

● text-align: start。

● text-align: end。



兼容性

text-align支持start和end属性值的兼容性如表3-3所示。

表3-3 text-align支持start和end属性值的兼容性(数据源自MDN网站)



使用绝对定位的时候经常会用到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逻辑属性大同小异,这里就不一一展开说明了。