Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

你可能不知道的 vertical-align #12

Open
zhuping opened this issue Dec 13, 2019 · 0 comments
Open

你可能不知道的 vertical-align #12

zhuping opened this issue Dec 13, 2019 · 0 comments
Labels

Comments

@zhuping
Copy link
Owner

zhuping commented Dec 13, 2019

自从换了工作后,每天都有忙不完的业务代码要写,已经很久没有学/写点东西了。刚好最近在组内做了一次小分享,那就把这次分享的内容整理成文吧。可能很多人会和我一样,平时对 css 的关注度没有像其他技术一样那么多,很多都停留在【能用就行】的阶段,对 css 背后的一些明文规范也不是很清楚。那么今天我们就拿 vertical-align 来说下吧,大家都知道它可以用来指定元素的垂直对齐方式,但是你平时真的有用对吗?

问题现象

我们先来看几个例子,都很简单而且平时会经常碰到。

  • Demo1:两个并排的 div 元素,设置为 inline-block ,可以看到能水平对齐展示

  • Demo2:同样是两个设置为 inline-blockdiv,但这次右边的 div 内增加两个汉字,导致右边的 div 跑到了下边。

  • Demo3:在 Demo2 的基础上,我们对第二个 div 添加了一个 class,class 的内容是 vertical-align:top,神奇的让右边的 div 再次和左边的 div 水平对齐了。

  • Demo4:在 Demo2 的基础上,我们在左边的 div 内也加入两个汉字,也可以达到和 Demo3 一样的效果。

例子真的都很简单,没有引入任何复杂的其他样式来干扰我们的测试,但通过添加汉字和一句 vertical-align 实现了两个 div 从对齐到不对齐,再到对齐,是不是很神奇 :)

vertical-align 到底是什么

既然今天讨论的是 vertical-align,那 vertical-align 到底是什么呢?来自 MDN 上的介绍:

用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方式

我们再来看下 w3c 上的规范文档

从文档上可以看到,value 值有 baselinesubsuper...,初始值为 baseline,仅作用于 inline-leveltable-cell 的元素,也就是说对于 block 元素是不起作用的,无继承,如果后面跟的是百分比值,那么取决于元素本身的 line-height 值。

初始值为 baseline 基线,那可能会疑惑什么是基线呢?可以通过下面这张图了解:

它是英文字母小写的 x 下边缘线就是基线,而且字母 x 的高度也称为 x-height

属性说明

属性值 描述
top Align the top of the aligned subtree with the top of the line box.(使元素及其后代元素的顶部与整行的顶部对齐)
bottom Align the bottom of the aligned subtree with the bottom of the line box.(使元素及其后代元素的顶部与整行的顶部对齐)
baseline Align the baseline of the box with the baseline of the parent box. If the box does not have a baseline, align the bottom margin edge with the parent's baseline. (将元素的基线与父元素的基线对齐。 如果该元素没有基线,则将下边距边缘与父元素的基线对齐)
middle Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent. (将元素的中点与父元素的基线加上父元素 x-height 的一半对齐)
sub Lower the baseline of the box to the proper position for subscripts of the parent's box. (This value has no effect on the font size of the element's text.) (使元素的基线与父元素的下标基线对齐,对元素文本的 font size 无影响)
super Raise the baseline of the box to the proper position for superscripts of the parent's box. (This value has no effect on the font size of the element's text.) (使元素的基线与父元素的上标基线对齐,对元素文本的 font size 无影响)
text-top Align the top of the box with the top of the parent's content area. (使元素的顶部与父元素的字体顶部对齐)
text-bottom Align the bottom of the box with the bottom of the parent's content area. (使元素的底部与父元素的字体底部对齐)
<percentage> Raise (positive value) or lower (negative value) the box by this distance (a percentage of the 'line-height' value). The value '0%' means the same as ‘baseline’. (表示升高(正值)或者降低(负值)元素的距离(相对于 line-height 的值),0% 与 baseline 等同)
<length> Raise (positive value) or lower (negative value) the box by this distance. The value '0cm' means the same as ‘baseline'. (表示升高(正值)或者降低(负值)元素的距离,0 cm 和 baseline 等同)

上面列举了 value 值的一个解释说明,只要记个大概就好,后面真正使用的时候,直接套这里的规范来就行,但值得关注的是,里面缺少了 topbottom 两个值,而且上面的对齐方式都是参考父元素。

属性值 描述
top Align the top of the aligned subtree with the top of the line box.(使元素及其后代元素的顶部与整行的顶部对齐)
bottom Align the bottom of the aligned subtree with the bottom of the line box.(使元素及其后代元素的底部与整行的底部对齐)

之所以要把 topbottom 单独拎出来讲,是因为它俩的对齐方式与其他值不一样,它俩是使元素相对于线框(line box)对齐,至于线框,可以理解为内联级元素在一行中一个挨一个地排列,一旦当前行放不下了,就在它下方创建一个新行,所有这些行就是所谓的线框(line box),用来包住这一行的所有内容。不同大小的内容意味着不等高的线框,下图红色的线就是线框边界,可以看出它的高度取决于最顶部内容和最底部内容的差。

另外,我们在 w3c 的文档上还能看到两句总结性的结论

  • The baseline of an 'inline-table' is the baseline of the first row of the table.
  • The baseline of an 'inline-block' is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its 'overflow' property has a computed value other than 'visible', in which case the baseline is the bottom margin edge.

第一句说 line-table 元素的基线取决于表格第一行的基线,这个不是我们本期讨论的重点。我们再来看下第二句,inline-block 元素的基线是常规流中其最后一个线框的基线,如果它没有常规流中的线框,或者是其 overflow 属性的值不是 visible ,基线则是它下边距的边界。

问题解释

有了上面的理论知识,我们再回头看下最前面的几个问题,试着用上面的知识去解释下现象。

  • 问题一:

inline-block 元素的基线是最后一个 line box 的基线,当前 div 都为空,也就是没有线框,所以当前元素的基线是下边距边界,默认都是以 baseline 方式对齐,所以两个 div 基线对齐,就是呈现水平对齐了。为了验证上面的解释,我们把第二个 div 的高度设置为 200px ,可以看到仍旧是底部对齐,说明解释是对的。

  • 问题二:

第一个 div 没有内容,所以基线是下边距边界,第二个 div 包含了内容文本,所以它的基线是最后一行的 line box 基线,两个 div 都是以默认 baseline 对齐方式,所以呈现第一个 div 的下边距边界与第二个 div 的内容基线对齐。同样为了验证上面的解释,我们在第二个 div 内写入两行文本,可以看到左边的 div 与第二个文本底部对齐了;或者是对左边的 div 添加一个下边距,可以看到下边距与右边的文本底部对齐了,所以两个 case 都再次证明上面的解释是正确的。

  • 问题三:

第一个 div 还是没有内容,所以基线仍旧是底部下边界。第二个 div 这次加了 vertical-align:top,所以它会使元素及其子元素的顶部,与线框 line box 的顶部对齐,这里的线框指的是两个 div 所在的线框,那顶部边界也就是第一个 div 的顶部,所以呈现两个 div 顶部对齐。同样,为了验证我们把第二个 div 的高度设置为 150px,结果发现还是顶部对齐排列,所以上面的分析也是正确的。

  • 问题四:

这个就比较简单了,两个 div 都有内容,所以它们的基线都是最后一个线框 line box 的基线,默认都是 baseline 方式对齐,所以两个 div 的基线对齐,因此两个 div 也对齐。为了验证是与最后一个线框对齐,我们在左边的 div 内再加入一行文字,可以看到这次左边的最后一行与右边的最后一行对齐了,所以确实是基于最后一个线框对齐。

思考题

通过上面案例的分析,大家是不是对 vertical-align 有了一些了解呢?那不妨来思考一下下面这个案例为什么能对齐:

还是由两个 inline-block 的 div 组成,左边的无内容,右边的包含一行文本,且两个 div 都被设置了 vertical-align:middle 属性,那为什么这样也可以对齐呢?

我们试着方向推导下,如果都去掉 vertical-align:middle 属性,由问题二分析可知会如下呈现

然后我们再来回顾下,vertical-align:middle 是将元素的中点与父元素的基线加上父元素 x-height 的一半对齐,所以知道父元素基线在哪成了关键。这里的父元素是 body,那怎么获取父元素的基线呢?这里有个技巧,因为基线是字母 x 的下边缘线,所以给 body 添加一个 after 伪类,content 内容为 x,这样就能知道父元素的基线在哪里了
-w1166

那么我们试着给左边的 div 加一个 vertical-align:middle,同时为方便理解,把左边 div 的中间显示出来,
-w1265

可以看到左边的 div 的中点与父元素基线加上父元素 x-height 的一半对齐了,然后再对右边的 div 同样添加 vertical-align:middle,这样右边的 div 的中点也同父元素基线加上父元素 x-height 的一半对齐,所以最终两个 div 对齐。

参考

@zhuping zhuping added the css label Dec 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant