2021-07-16

CSS vertical-align属性浅谈

前置准备

vertical-align可以说很有趣的,光看表面意思觉得还一旦行用起来是经常不灵,学CSS的时候也不怎么重视和深入探究,其实一步步剖析掰开了就明了了,在认识 vertical-align 属性之前,首先来了解一些概念:

对于行内级元素

行内级盒子:由行内级元素生成

行内盒子:参与行内格式化上下文创建的行内级盒子称为行内盒子,由非替换元素生成,属于行内级盒中的一种。比如所有具有 display:inline 样式的非替换盒子

  • 具名行内盒子

  • 匿名行内盒子

    类似于匿名块盒,CSS引擎有时候有必要生成匿名行内盒。常见的例子是块级盒直接包含文本,那么这些文本将包含在匿名行内盒子中。(空白如果使用white-space去掉,则不会生成匿名行内盒)

原子行内级盒子:不参与行内格式化上下文创建的行内级盒子。比如可替换的行内级元素img、行内块元素 inline-block 、行内表格元素 inline-table ,这些元素创建的盒子不会像行内盒子一样可以被拆分为多个盒子。

<style>
    p{
        width: 200px;
        height: 100px;
        border: 2px solid #03A9F4;
    }
    span{
         background-color: aqua;
    }
</style>
<body>
    <p>
        <strong>匿名行内盒</strong>
        继承所有可继承的属性,非继承的属性取
        <span>初始值(initial value)</span>
    </p>
</body>

在这里插入图片描述

span{
    display: inline-block;
}

在这里插入图片描述
行盒:行盒(ine box) 是指一行的一个虚拟的矩形框,由该行中行内元素组成。每行都会生成一个行盒

在这里插入图片描述

  • 不严谨说法:行盒的高度等于这一行中行内元素line-height最大值
  • 严谨说法:行盒最终高度受行内各元素vertical-align影响,是这一行行内盒子的垂直最高边界到最低边界的距离

计算行盒高度过程

1、先计算行盒内每个行内级盒子的高度。对于可替换行内元素img、行内块元素以及行内表格元素,高度是其外边距盒的高度;对于不可替换行内元素(display:inline),高度是其 line-height

2、行内级盒子根据其 vertical-align 属性垂直对齐,再计算行内盒子垂直的最高边界到最低边界的距离

行高:用于设置多行元素的空间量,如文本的间距

  • 对于块级元素,它指定元素行盒的最小高度
  • 对于非替代的 inline 元素,它用于计算行盒的高度,即使行高设定的比内容区高度还要小,行内盒的高度仍然是由line-height决定

内容区:是一种围绕文字的盒子,在字体设计中称em框(em square),大小与font-size和font-family相关,可以把em框理解为字符未设置padding时的background大小,但是设置padding撑起字符的background绝不代表em框的大小被改变了

基线baseline:书写英语字母时,小写英文字母 X 底部所在的位置

中线:基线+1/2字母x的高度的位置

在这里插入图片描述

vertical-align

定义:指定行内元素在其所在行盒中的垂直对齐方式,在表格单元格中,指定单元格框中的单元格内容的对齐方式

作用元素:display属性值为inline、inline-block、inline-table另加一个table-cell的元素

取值:baseline、middle、top | bottom、text-top | text-bottom、sup | super、<length>长度值、<percentage>百分数

一、baseline

默认值,使子元素的基线父元素的基线对齐

子元素的基线

inline元素:字母 x 的下边线即为基线。汉字或部分英文字母的下边线要低于字母 x 的下边线,但仍然以字母 x 的下边线为标准

inline-block元素:Inline-block元素的基线取决于元素是否具有in-flow内容

在这里插入图片描述

红线:元素外边缘,即margin-box的顶部和底部边缘
蓝线:基线

  • 有in-flow内容,inline-block元素的基线是普通流中最后一个内容元素的基线(左边那个),如input、select为代表的替换元素

  • 有in-flow内容并且overflow值不是visible,基线在margin-box的底部边缘那个位置(中间那个)

  • 没有in-flow内容,基线也是在margin-box的底部边缘那个位置(右边那个)—还有img、iframe、canvas、video、textarea等

流外的元素(out-of-flow):

  • 浮动的(float:left/right),绝对定位的(position:absolute/fixed)或者是根元素(html),out-of-flow会强制当成 block 方式处理,因此,vertical-align 也就失去了作用

流内的元素(in-flow):

  • 一个元素不是流外的元素,那么它被称之为流内的元素(in-flow)
div{
    width: 500px;
    border: 2px solid #03A9F4;
    text-align: center;
    margin: 50px auto;
    position: relative;
}
span{
    width: 100px;
    height: 100px;
    display: inline-block;
}
span:first-child{
    background-color: deeppink;
}
span:nth-child(2){
    background-color: aqua;
    overflow: auto;
}
span:nth-child(3){
    overflow: visible;
    background-color: red;
}
span:nth-child(4){
    margin-bottom: 20px;
    background-color: pink;
}
div::before{
    content: '';
    left: 0;
    top: 119px;
    width: 100%;
    height: 1px;
    position: absolute;
    background-color: black;
}
<div>
    <span>我有内容x</span>
    <span>overflow: auto</span>
    <span>overflow: visible</span>
    <span></span>
</div>

在这里插入图片描述
注意:HTML规范没有详细说明部分可替换元素的基线,如 <textarea> ,这意味着这些元素使用此值的表现因浏览器而异。

父元素的基线

父容器最开始会有一个不可见的、零宽度的透明节点行内盒子,官方称它strut,可以看成普通文本比如插入虚拟的小写字母x,它继承了父元素的font-family、font-size、line-height属性

行内元素其实就是与这个隐藏的strut的对应参考线对齐。

这个行内盒子在每个行盒开始地方,所以本质上vertical-align是指定行内元素在其所在行盒中的垂直对齐方式

注意:父元素基线的绝对位置永远不会改变,但在行盒中相对位置会改变

回头说说line-height

对于非空的块级元素:line-height 指定了该容器内行盒的最小高度

为何这样说?

line-height的默认值是normal,由字体字号决定,line-height的计算值就是该元素的字体的 font-size x normal

谷歌浏览器默认font-size为16px,字体为微软雅黑

div{
    width: 100px;
    background-color: pink;
}
span{
    font-size: 12px;
    background-color: aqua;
}
<div>
    <span>不是我撑开div的</span>
</div>

支柱的行内盒行高为: 16 x 微软雅黑normal 1.32 = 21.12px
span的行内盒行高为: 12 x 微软雅黑normal 1.32 = 15.84px

在这里插入图片描述
块级元素没有指定高度的时候,是由内部所有行盒的高度撑开的,每行行盒的高度需要包含一行中所有inline-box的上下边界,此例行盒上下边界为支柱生成行内盒子的上下边界

为何强调非空?

div{
    width: 100px;
    line-height: 100px;
    background-color: pink;
}
<div>
    <span></span>
</div>

在这里插入图片描述
可以看到此时即使指定了line-height容器也是没有高度

回到baseline

.line-box{
    width: 400px;
    position: relative;
    border: 2px solid #03A9F4;
}
img{
    width: 100px;
}
span:nth-child(2),span:nth-child(3),
span:nth-child(4),span:nth-child(6){
    display: inline-block;
}
span:nth-child(3),span:nth-child(6){
    width: 100px;
    height: 100px;
}
span:nth-child(2){
    line-height: 2;
    font-size: 20px;
    background-color: aqua;
}
span:nth-child(3){
    line-height: 1.5;
    background-color: pink;
}
span:nth-child(4){
    width: 50px;
    height: 50px;
    background-color: deeppink;
}
span:nth-child(6){
    background-color: yellow;
}
div::before{
    content: '';
    top: 99px;
    width: 100%;
    height: 1px;
    position: absolute;
    background-color: black;
}
<div class="line-box">
    <span>x</span>
    <span>x基线对齐</span>
    <span>最后一个内容x的基线</span>
    <span></span>
    <img src="images/cat.jpg" alt="萌萌哒">
    <span></span>
    <span>x</span>
</div>

在这里插入图片描述

二、middle
  • 子元素的中垂点与 父元素的基线往上 1/2x-height 处对齐,可以看作是字母x的中心位置对齐

  • 对于table-cell元素,指的是单元格填充盒子相对于外面的表格行居中对齐

.line-box{
    width: 400px;
    position: relative;
    border: 2px solid #03A9F4;
}
span:first-child{
    background-color: aqua;
}
span:last-of-type{
    width: 100px;
    height: 100px;
    display: inline-block;
    background-color: pink;
}
span,img{
    vertical-align: middle;
}
<div class="line-box">
    <span>x</span>
    <span></span>
    <img src="images/20.PNG">
</div>

在这里插入图片描述
经典案例:图片底下存在空隙

div{
    width: 400px;
    background-color: pink;
    border: 2px solid black;
}
<div>
    <img src="images/cat.jpg" alt="萌萌哒">
</div>

在这里插入图片描述
前面说过,在每行行盒开始的地方有一个透明节点支柱,可以看作小写字母x,图片默认基线对齐即与x文字底部对齐,透明节点继承父元素的字体、字号、行高属性,所以透明节点的基线下边缘还有高度,高度可由这三个属性决定

span{
    background-color: #03A9F4;
}
<div>
    <span>x</span>
    ...
</div>

在这里插入图片描述
题外话:根据字体设计x文字基线底部的高度其实可以算出来

加大透明节点的行高,可以看到底部间隙加大了

div{
    line-height: 50px;
}

在这里插入图片描述
span设置成inline-block元素,让背景填充到line-height区域

在这里插入图片描述
可以看到图片底部的间隙其实是line-height和vetical-align属性造成的

解决方法很多种:

  • 图片设置vertical-align: middle | top | bottom 等(推荐使用)
  • 图片设置display: block,vetical-align只对行内元素起作用
  • 容器line-height足够小,只要半行间距小到字母x的下边缘位置或者再往上, 自然就没有了撑开底部间隙高度空间了,比如: 容器设置line-height: 0;但影响图文布局
  • 容器font-size足够小,line-height的值是基于font-size的,因此,在没有设置line-height的情况下font-size设为0也可以达到同样的效果。但同样影响图文布局
    在这里插入图片描述

经典案例:vertical-align: middle有时候不起效果

div{
    width: 400px;
    height: 300px;
    border: 2px solid #03A9F4;
}
span:last-child{
    width: 50px;
    text-align: center;
    display: inline-block;
    background-color: pink;
}
img{
    vertical-align: middle;
}
<div>
    <span>x</span>
    <img src="images/20.PNG">
    <span>x</span>
</div>

在这里插入图片描述
实际上,vertical-align:middle是起作用,因为父元素的基线即x文字底部的相对位置也会随着行高的改变而变化,此时行高太小导致基线的位置在这里,而中线在x底部往上x-height一半高度,也就是说中线位置更偏上了

给父元素的行高设置为父级的高度,使基线相对位置往下偏移

span:nth-child(1){
    background-color: aqua;
}
div{
    line-height: 300px;
}
div::before{
    content: ""; 
    display: inline-block;
    position: absolute;
    top: 149.5px;
    width: 400px;
    height: 1px;
    background-color: fuchsia;
}

在这里插入图片描述
可以看到只是近似垂直居中,因为文字具有下沉的特性,与字体设计有关

文字的垂直中心点比中线往下沉一点,不同字体的文字下沉的幅度不同,同时,文字大小越大,下沉越明显

font-size: 100px;

在这里插入图片描述
可以明显的观察到,图片的上部空白比下部空白要大。其偏差正好是文字下沉的偏差

解决方法

1、font-size:0,设置字号为0,基线、x-height线等等线都合为一体(因为它们之间的距离为0)。所以1/2 x-height的位置也变成在行内最中间的那里,图片就完美居中了。但不推荐

2、添加辅助元素(推荐伪元素),去掉上一步设置的父元素行高

  • 辅助元素inline-block化
  • 辅助元素宽度为0,高度为100%
  • vertical-align:middle

这里设置宽度为1px是辅助阅读,即使width是0px浏览器渲染结果也如下图

div::after{
    content: '';
    display: inline-block;
    height: 100%;
    width: 1px;
    background-color: black;
}

在这里插入图片描述
添加父元素同高宽度为0的行内块(伪)元素那么基线在底部,即父容器底部,又默认与行盒基线对齐,所以行盒基线x文字底部此时相对位置也移动到父容器底部

至于图片移动是因为子元素的对齐方式影响了行盒基线x文字底部的相对位置,其它兄弟元素的对齐方式是根据行盒基线的,所以说间接影响了图片,导致图片下移

div::after{
    ...;
    vertical-align: middle;
}

伪元素中线正好与文字垂直中心点对齐
在这里插入图片描述

三、top | bottom
  • top:元素inline-box的顶部与行盒顶部对齐
  • bottom:元素inline-box的底部与行盒底部对齐
  • 对于table-cell元素,指的是元素的padding上边缘和表格行的顶部对齐
div{
    width: 500px;
    margin: 50px auto;
    border: 2px solid #03A9F4;
}
span:first-child{
    background-color: pink;
}
span:nth-child(2){
    line-height: 60px;
    background-color: red;
}
span:not(span:last-of-type){
    padding: 20px 20px;
    vertical-align: top;
}
span:nth-child(3){
    width: 100px;
    height: 100px;
    margin-bottom: 20px;
    vertical-align: bottom;
    display: inline-block;
    background-color: aqua;
}
div::after{
    content: 'x';
}
<div>
    <span>x</span>
    <span>x</span>
    <span></span>
    <img src="images/20.PNG">
</div>

在这里插入图片描述
可以看到

  • 第一个span的inline-box的顶部与行盒顶部对齐,前面知道inline元素的inline-box高度计算是其line-height,尽管inline元素可以有内边距边框然而与计算行内盒子的高度无关
  • 第二个span设置了60px的line-height,inline-box的顶部与行盒顶部对齐如图所示

如果图片也设置top或者bottom对齐方式结果如何呢?

img{
    vertical-align: top;
}

在这里插入图片描述
为何添加的辅助能看父容器基线的透明节点支柱会改变位置?

我们先说说父容器基线的相对位置变化问题

父容器基线的相对位置随着行盒的高度改变而变化

前面图片底部存在空白间隙的案例,如果没有图片,则行盒的高度最小为支柱高度

在这里插入图片描述
如果有图片

在这里插入图片描述
可以看到父容器基线的相对位置变化了

而行盒的高度是一行中所有的inline-box的最高边界到最低边界,即元素inline-box大小和元素的对齐方式也会影响父容器基线的相对位置

自己理解综合影响父元素的基线相对位置有:

  • 某个行内级子元素的inline-box大小和对齐方式
  • 父元素的font-size (前面文字下沉的案例)
  • 父元素的line-height(前面middle无效的案例)

为什么总是强调相对位置的原因了,绝对位置是永远看作x文字底部的线,其它线基于此

回过头说说上述透明节点支柱会改变位置的问题(个人观点抛砖引玉,各位有更好的观点不妨指点)

当行盒内元素的对齐方式不是基于父容器基线的时候,父容器基线相对位置由高度最大的inline-box决定

当其它的子元素如前两个span以基线对齐时:

span:not(span:last-of-type){
    padding: 20px 20px;
    /* vertical-align: top; */
}

在这里插入图片描述
再加上第三个span

span:nth-child(3){
...;
/* vertical-align: bottom; */
}

在这里插入图片描述
此时父容器基线相对位置就不由高度最大的inline-box决定了

四、text-top | text-bottom
  • text-top:元素行内盒子的顶部和父元素内容区(即透明节点看作虚拟字母x的内容区或em框)顶部对齐
  • text-bottom:元素行内盒子的底部和父元素内容区(即透明节点看作虚拟字母x的内容区或em框)底部对齐

MDN说是与父元素的字体顶部或底部对齐

先说说元素行内盒子的顶部,在标准浏览器下即line-height区域顶部

div{
    width: 500px;
    height: 200px;
    margin: 50px auto;
    border: 2px solid #03A9F4;
}
span:first-child{
    background-color: pink;
}
span:last-child{
    padding: 20px 0;
    vertical-align: text-top;
    background-color: aqua;
}

<div>
    <span>父元素字体</span>
    <span>子元素</span>
</div>

在这里插入图片描述
可以看到只有行内盒子的顶部对齐,前面说过inline元素的行高为line-height计算值部分,与内边距边框无关

span:last-child{
    vertical-align: text-top;
    background-color: aqua;
    font-size: 50px;
}

在这里插入图片描述

div{
...;
line-height: 200px;
}
span:last-child{
    /* font-size: 50px; */
}

在这里插入图片描述
这里让第二个span变成inline-block元素,可以看到背景透过行高区域,正好是顶部对齐,就不测试了

text-bottom同理,只不过是底部了,也不再测试了

再说说父级字体,其实就是透明节点支柱strut,它继承了父元素的字体,字体设计都会有个em-square字符框,inline元素文字的内容区,注意开头说过它不是鼠标选中的蓝色背景区域,虽然大多数情况下是

div{
    line-height: 200px;
    font-size: 32px;
}
span:last-child{
    display: inline-block;
}

在这里插入图片描述
此时透明节点继承了父元素的行高和字号,内容为父元素字体的那个元素代表透明节点的,可以看到父元素字体顶部即内容区或者说em-square字符框

五、sup | super
  • sub:使元素的基线与父元素的下标基线对齐
  • super:使元素的基线与父元素的上标基线对齐
div{
    width: 500px;
    font-size: 20px;
    text-align: center;
    margin: 50px auto;
    border: 2px solid #03A9F4;
}
span:nth-of-type(2),span:nth-of-type(3){
    width: 100px;
    height: 100px;
    display: inline-block;
}
span:nth-of-type(2){
    vertical-align: sub;
    background-color: pink;
}
span:nth-of-type(3){
    vertical-align: super;
    text-align: right;
    background-color: aqua;
}
<div>
    <span>父级</span><sub>下标x</sub>
    <span>对齐父级下x标基线</span>
    <span>对齐父级上标基线x</span>
    <sup>x上标</sup><span>父级</span>
</div>

在这里插入图片描述
六、<length>长度值

  • 使元素的基线对齐到行盒基线之上的给定长度。可以是负数。如果为0,和vertical-align:baseline一样

验证行盒最终高度受行内各元素vertical-align影响,是这一行行内盒子的垂直最高边界到最低边界的距离

div{
    width: 500px;
    text-align: center;
    border: 2px solid #03A9F4;
}
span:first-child{
    line-height: 30px;
    vertical-align: 20px;
    background-color: pink;
}
span:last-child{
    line-height: 50px;
    vertical-align: -20px;
    background-color: aqua;
}
div::before{
    content: 'x';
}
<div>
    <span>行内盒子1,行高为30px</span>
    <span>行内盒子2,行高为50px</span>
</div>

在这里插入图片描述
div中的行内盒子行高最大是50px,但由于这个行内框存在vertical-align偏差,导致行盒的高度大于50px

七、<percentage>百分数
  • 使元素的基线对齐到行盒基线之上的给定百分比,该百分比是line-height属性的百分比。可以是负数。如果百分比为0%,就和vertical-align:baseline一样
div{
    width: 400px;
    height: 200px;
    line-height: 100px;
    border: 2px solid #03A9F4;
}
span:not(:first-child){
    width: 100px;
    height: 100px;
    display: inline-block;
}
span:nth-child(2){
    vertical-align: -50%;
    background-color: aqua;
}
span:last-child{
    vertical-align: 50%;
    background-color: pink;
}
<div>
    <span>x</span>
    <span></span>
    <span></span>
</div>

在这里插入图片描述

八、vertical-align与表格单元格

一般用的

  • top:使单元格内边距的上边缘与该表格内容顶部对齐
  • middle:使单元格内边距盒模型在该表格内容居中对齐
  • bottom:使单元格内边距的下边缘与该表格内容底部对齐

表格单元格中默认就是垂直居中

table{
    min-width: 400px;
    margin: 50px auto;
    border-collapse: collapse;
    box-shadow: 0 0 20px rgba(0,0,0, 0.15);
}
tr{
    border-bottom: 1px solid black;
}
th,td{
    width: 180px;
    height: 80px;
    padding: 10px 15px;
    border-right: 1px solid black;
}
td{
    text-align: center;
}
th:last-of-type,td:last-child{
    border: none;
}
thead tr:only-child{
    color: white;
    background-color: #03A9F4;
}
tbody tr:last-of-type{
    border-bottom: 2px solid #03A9F4;
}
<table>
    <thead>
        <tr>
            <th>序号</th>
            <th>商品名称</th>
            <th>金额/元</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>西瓜</td>
            <td>36</td>
        </tr>
        <tr>
            <td>2</td>
            <td></td>
            <td>9</td>
        </tr>
        <tr>
            <td>3</td>
            <td>苹果</td>
            <td>19</td>
        </tr>
    </tbody>
</table>

在这里插入图片描述

tbody tr:first-child :first-child{
    background-color: pink;
    vertical-align: top;
}
tbody tr:nth-child(2) :first-child{
    background-color: aqua;
    vertical-align: middle;
}
tbody tr:nth-child(3) :first-child{
    background-color: deeppink;
    vertical-align: bottom;
}

在这里插入图片描述
baseline、sub、super、text-top、text-bottom、<length><percentage>就不试了

作者:用户7588300048329
链接:https://juejin.cn/post/6984379930226524190
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值