深入理解css中vertical-align属性

一、为什么要写这篇文章

今天看到一个问题:

两个 div 都设置 display:inline-block,正常显示;但是在第二个 div 中加一个块级元素或者内联元素,显示就变了个样,为什么?

<meta charset="utf-8"/>
<style>
div{
    width: 100px;
    height: 100px;
    border:1px solid red;
    display: inline-block;
}
.align{
/*    vertical-align: top;*/
}
</style>
<body>
    <div>
    </div>
    <div class="align">为什么?</div>
</body>

 

解决方案就是给第二个 div 加上:vertical-align:top。

关于 vertical-align 和基线我知道一点,但是这个问题我没能答出,所以学习总结分享一下。

二、vertical-align 干什么的?

w3c有一段相关信息如下:

'vertical-align'
Value:      baseline | sub | super | top | text-top | middle | bottom | text-bottom | <percentage> | <length> | inherit
Initial:      baseline
Applies to:      inline-level and 'table-cell' elements
Inherited:      no
Percentages:      refer to the 'line-height' of the element itself
Media:      visual
Computed value:      for <percentage> and <length> the absolute length, otherwise as specified

可以看到 vertical-align 影响inline-level 元素和 table-cell 元素垂直方向上的布局。根据MDN描述,vertical-align 对::first-letter 和::first-line 同样适用。

适用于:

inline 水平的元素

  • inline:<img>,<span>,<strong>,<em>, 未知元素
  • inline-block:<input>(IE8+),<button><IE8+>....

'table-cell' 元素

  • table-cell:<td>

所以默认情况下,图片,按钮,文字和单元格都可以用 vertical-align 属性。

取值:

vertical-align: baseline|length|percentage|sub|super|top|middle|bottom|text-top|text-bottom|initial|inherit;

三、baseline

1、字母‘x’与 baseline

 字母 x 的下边缘(线)就是基线。不是字母s之类下面有尾巴的字母

基线甚至衍生出了:

  1. “alphabetic” baseline: “字母”基线 – 英文
  2. “hanging” baseline: “悬挂”基线 – 印度文
  3. “ideographic” baseline: “表意”基线 – 中文

2、baseline 的确定规则


1、inline-table 元素的 baseline 是它的 table 第一行的 baseline。

2、父元素【line box】的 baseline 是最后一个 inline box 的 baseline。 

3、inline-block 元素的 baseline 确定规则

  • 规则 1:inline-block 元素,如果内部有 line box,则 inline-block 元素的 baseline 就是最后一个作为内容存在的元素 [inline box] 的 baseline,而这个元素的 baseline 的确定就要根据它自身来定了。
  • 规则 2:inline-block 元素,如果其内部没有 line box 或它的 overflow 属性不是 visible,那么 baseline 将是这个 inline-block 元素的底 margin 边界。

3、例子:inline-block 例子

上图描述:

上图中从左到右都是 line-block 元素,红线代表 margin-box 的边界,蓝线代表 baseline;黄色为 border,绿色为 padding,蓝色为 content。

左边元素包含着没有脱离正常流的内容 c,中间元素除了没有脱离正常流的内容 c 外还增加了overflow:hidden,右边元素没有内容,但是内容区有宽高

分析图中各种情况 inline-block 元素的 baseline

上图左图,inline-block 元素有处于正常流的内容,根据规则 1,所以 inline-block 的 baseline 就是最后一个作为内容存在的元素的 baseline,也就是内容 c 的 baseline,而 c 的 baseline 根据自身定,就是图中蓝色。

上图中图,inline-block 元素 overflow:hidden 不为 visible,根据规则 2,该 inline-block 元素 baseline 就是 inline-block 元素的 margin-box 的下边界了,即图中蓝线。

上图右图,inline-block 元素没有内容,根据规则 2,所以其 baseline 为 margin-box 的下边界,即蓝线。

4、例子:baseline 确定规则例子 

举例:

<style type="text/css">
    .ctn-block{
        display: block;
        background-color: #bbb;
        line-height: 200px;
        font-size: 50px;
    }
    .ctn-block .child1{
        display: inline-block;
        width: 100px;
        height: 100px;
        margin:10px 0;
        vertical-align: baseline;
        background-color: aliceblue;
    }
</style>

<div class="ctn-block">
<div class="child1"></div>
<span>Gg</span>
</div>

View Code

 

分析:

父元素.ctn-block 的 base-line 是Gg的 baseline,

inline-block 元素因为没有内部 line box,也没有设置 overflow:visible,所以其 baseline 是底 margin 边界。

四、vertical-align 基于 baseline 的不同取值

1、baseline

将子元素盒子的 baseline 与父盒子的 baseline 对齐。

2、middle

将元素盒子的垂直中点与父盒子的baseline加上父盒子的 x-height 的一半位置对齐

这里元素盒子的垂直中点容易确定,父盒子的 baseline 也好确定,但是 x-height 要进行计算得到,这个 x-height 就是字母 x 的高度。

 

3、text-top

将盒子的顶端 (margin-top 边界) 与父盒子的文本区域顶端对齐

审查盒子看到 margin-top 的顶端。

审查文本,看到蓝色区域的上边界就是文本区域顶端。

最终效果就是盒子的顶端与父盒子文本区域顶端对齐。

4、text-bottom

将盒子的底端(margin-bottom 边界) 与父盒子的文本区域底端对齐

和 text-top 类似,不过将子元素的 margin-bottom 和文本区域的下边界对齐。

5、sub

将子元素盒子的 baseline 降低,到适当的父盒子的下标位置

子元素的 baseline 已经确定了,就是 margin-bottom 下边界,但是父盒子的下标位置太不好理解。。。首先需要了解下标这个概念,我们可以通过 <sub> 标签为文字添加下标,将 <span> 中的内容修改为 Gg<sub>Gg</sub>,就会有如下效果。

这里就是将元素的 margin-bottom 下边界和下标的 baseline 对齐。

6、super

将元素盒子的 baseline 升高,到适当的父盒子的上标位置。

与 sub 对应,super 提升到上标内容的 baseline 处,首先通过 <sup> 标签创建上标。

 

7、percentage

百分比:升高(正值)或降低(负值)子元素盒子,具体的升高 / 降低数值由父盒子的 line-height 的值乘以百分比计算得出。如果百分比为 0%,就和 vertical-align:baseline 一样。

这个是相当好理解的,就相当于子元素盒子的 baseline 升高或降低,具体数值为百分比乘以父盒子的 line-height。

本例中,父盒子的 line-height 为 200px,所以设定 25%,元素应该上移 50px。

并不是很直观,给它加上一个 transform: translate(0, 50px);【相对下移 50px】,它又移到那个熟悉的位置了。

 

8、length

升高(正值)或降低(负值)子元素盒子。值为升高 / 降低的距离,如果为 0,和 vertical-align:baseline 一样。

以我们最常用的 px 作为单位,设定 vertical-align:50px,效果就和上面百分比为 25%(200px*25%=50px)一样了,不做例子了。

五、vertical-align 基于 line box 的不同取值

当 vertical-align 设置为 top 和 bottom 时,其就不是按照 baseline 进行定位了,而是根据 line box 进行定位。子元素盒子的顶部和底部也就是其上下 margin 外边界。

1、top

将子元素盒子的顶部和其所在的 line box 顶部对齐

由于 vertical-align:top 将会让子元素盒子顶部与 line box 顶部对齐,而如果 line box 高度小于子元素高度,line box 将会被撑开。我们先用一个高度较高的元素撑开 line box,然后看看效果:

 

可以看到,big 子元素撑开了 line box,而 child1 的 margin-top 外边界紧贴在 line box 的顶端。

2、bottom

将子元素盒子的底部和其所在的 line box 底部对齐

和 top 类似,由于 big 用于撑开 line box,可以不必修改其 vertical-align 的值,仅修改 child1 为 vertical-align:bottom,效果:

六、inline 元素下方可能会有一点空隙

例子:尝试将 li 元素在垂直方向上进行对齐的话,这个现象非常常见

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
        ul{
            background-color: bisque;
        }
        .box { display: inline-block;
            width: 100px;
            height: 100px;
            background-color: aliceblue;
            /*     vertical-align: middle;*/
        }
    </style>

</head>
<body>

<ul>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
</ul>
</body>
</html>

1、垂直空隙

 

因为 li 元素默认 vertical-align:baseline,而 baseline 的下方会给字母的一部分留出空间,因此会产生一个空隙,要产生理想的效果,解决方案就是改变 line box 的 baseline 位置,比如将这些 li 设置为 vertical-align:middle。【tip: 加一个 x 效果更明显】

2、水平空隙

li 元素的水平空隙是因为换行引起的,这个换行会变成一个空白,这个空白会被解析为 DOM 中的文本节点。比如像下面酱紫的代码。

<ul>
  <li class="box"></li><li class="box"></li>
  <li class="box"></li>
</ul>

效果如下:因为前 2 个 li 之间没有空白,而 2 和 3 个 li 之间有空白。

但是上面的代码可读性太差,也不美观,一般这样写

    <ul>
        <li class="box"></li><!-- 注释去空格
     --><li class="box"></li>
        <li class="box"></li>
    </ul>

我们用一个注释节点代替空白(文本节点),而注释节点渲染的时候是不渲染的。了解更多 DOM 中的节点类型,可看我的另一篇文章《DOM》

七、vertical-align:middle 让元素下移而不居中的问题分析

1、问题

现在有三个 inline-box 块,高度分别为 100px,200px,300px,想让高度为 100px 的块垂直居中,于是写出了如下代码:

<style type="text/css">
    .ctn-block{
        background-color: #bbb;
    }
    .ctn-block .child {
    display: inline-block;
    width: 100px;
    background-color: aliceblue;
}
.ctn-block .child-1 {
    height: 100px;
/*    vertical-align: middle;*/
}
.ctn-block .child-2 {
    height: 200px;
}
.ctn-block .child-3 {
    height: 300px;
}
</style>
----------------------------------------------
<div class="ctn-block">
    <div class="child child-1"></div>
     <div class="child child-2"></div>
    <div class="child child-3"></div>
</div>
View Code

给中间 div 加上 vertical-align:middle,效果变为上图二的样子——child-1 元素下移了,但是却没有居中。

2、原因

从上面可以指定,vertical-align:middle 的定位方式是:将子元素盒子的垂直中点与父盒子的 baseline 加上父盒子的 x-height 的一半位置对齐。

子元素盒子的中点很好算,而父盒子的 baseline 加上父盒子的 x-height 一半位置又是什么呢?

首先计算父盒子的 baseline:三个子元素的 baseline 走在一条直线上,就是 child-2 和 child-3 的底部。

然后加上父盒子的 x-height:由于 chrome 下默认 font-size 是 16px,而 font-family:sans-serif,所以 x-height 的一半大概是 3-4px,综上,按照如下方式对齐:

3、 解决方案

 一种方式是将最高的元素设为 vertical-align:middle。

然后将想要居中的也设定为 vertical-align:middle,其他的根据需要设定 vertical-align:top/bottom。

原理有点抽象:

首先明确一点:最高元素设定为 vertical-align:middle 后,这个元素对于 line box 来说,baseline 就是其中线。

其他元素设置 vertical-align:top/bottom 后,它们不影响 line box 的 baseline,所以再将需要设定垂直居中的元素也设定为 vertical-align:middle,它们的 baseline 必然在最高元素的 baseline 之上,所以会会被强制下移,进行居中。

.ctn-block .child-1 {
        height: 100px;
        vertical-align: middle;
    }
    .ctn-block .child-2 {
        height: 200px;
        vertical-align:top;
    }
    .ctn-block .child-3 {
        height: 300px;
        vertical-align: middle;
    }

 

4、衍生的一种可行的垂直居中方案

为父元素设定一个伪元素::after, 其高度为父元素的高度,display:inline-block, 将其设定为 vertical-align:middle 即可撑开 line box,同时 line box 的 baseline 为父元素高度一半的位置。然后设定子元素 vertical-align:middle,即可实现居中。

考虑兼容性的话,这里需要使用一些 hack,由于 IE8 不支持::after 伪元素,所以需要一个 span 来替代。而 display:inline-block 亦需要 hack。

我的《未知宽高图片垂直居中》一文中就用到这种方案。 

八、其他应用

ico 和文字对齐 

<style type="text/css">
    .pop-viphead-nologinbox {
        width:500px;
    }
.pop-viphead-nologin-icon </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">{</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    display</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">inline-block</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    width</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 14px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    height</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 14px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    background</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> url("images/not_login_tip_ico.png") no-repeat</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">}</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(128, 0, 0, 1)">

.pop-viphead-nologin-txt </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">{</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    display</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> inline-block</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    color</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> #333</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    font-size</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 12px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    margin-left</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">2px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">}</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(128, 0, 0, 1)">

.pop-viphead-nologin-btn </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">{</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    display</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> inline-block</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    margin-left</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 3px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">}</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(128, 0, 0, 1)">

.pop-viphead-nologin-btn a </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">{</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    display</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> block</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    width</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 76px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    height</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 25px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    line-height</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 25px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    color</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> #fff</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    text-align</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> center</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    background-color</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> #00adee</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    border-radius</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 1px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    font-size</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 12px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">}</span>

</style>

<div class="pop-viphead-nologinbox">
<div class="pop-viphead-nologin-icon"></div>
<span class="pop-viphead-nologin-txt">您还没有登录哦!</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="pop-viphead-nologin-btn"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;</span><span style="color: rgba(128, 0, 0, 1)">a </span><span style="color: rgba(255, 0, 0, 1)">href</span><span style="color: rgba(0, 0, 255, 1)">="javascript:;"</span><span style="color: rgba(255, 0, 0, 1)"> j-delegate</span><span style="color: rgba(0, 0, 255, 1)">="login"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>立即登录<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

</div>

View Code

我想让左边 ico 和文字,按钮都对齐。

.pop-viphead-nologin-icon,.pop-viphead-nologin-txt,.pop-viphead-nologin-btn{
        vertical-align: middle;
    }

 

 

参考资源:

baseline:

如果想要了解更多关于 baseline 的详细的信息,可以参照W3C 标准 css-inline 的 baseline 部分

更多关于 baseline 的确定方式可以参照W3C 标准 css-align 的 baseline-rules 部分

vertical-align:

天镶大神

垂直居中之 vertical-align 详解 CSS 布局之 vertical-align

MDN vertical-align

[翻译] 关于 Vertical-Align 你需要知道的事情

w3c line-height 和 vertical-align

 

这篇文章在草稿箱呆了快 2 年了,重新整理了一下,发布出来。

 

本文作者starof, 因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/4512284.html有问题欢迎与我讨论,共同进步。