完全了解vertical-align以及相关现象

为何写这篇文章

这一切都得从一个百思不得其解的问题提及。首先由于我一个错误的认知,觉得只要在一行里设置了line-height,那么这一行的文字都会居中显示,这种效果若是在字号同样的状况下是正常的,可是在一行里面若是有字号不同的文字,状况就不同了。css

第二个X并无垂直居中,此时我想到了vertical-align这个偏方,试着对后面那个X设置了vertical-align:middle,因而神奇的事情发生了,后面那个字没有像预期那样上移动,反而向下移动少量。html

what???这么神奇的吗?到底是什么魔力形成了这样的效果?为此我查阅了不少资料,终于理解这一现象的本质缘由,可是经我查阅的不少资料或多或少都说的不全、难以理解,甚至有一些错误,误导你们。所以我决定结合自身的思考,写一篇深刻浅出,通俗易懂的文章来彻底剖析涉及到vertical-align神奇现象的本质api

原理

正所谓授人授人以鱼不如授人以渔,了解一个现象的本质,才能在往后再次遇到相似的问题时再也不迷惑。而我通过深思熟虑后,对此类现象有了一个结论:一般在line box里遇到一些没法理解的现象,一般都与font-family font-size line-height vertical-align四个属性有关系。ide

能够说这四个属性是解释line box不少没法理解的表现的关键,在Deep dive CSS: font metrics, line-height and vertical-align这篇文章中详细解释了这个问题,这篇文章写的很是好,很是推荐你们阅读一遍,本文的一部分知识点都是来自于这篇文章,在文章里面详细描述的部分将会在本文中以我理解的方式来解释。wordpress

那么就来看看这四个属性的相关原理。字体

字体与字体大小

每个字都是由一个容器(em-square)包裹住的,而且这个容器内定义了ascender、descender、capital height、x-height等的字体度量。flex

这些字体度量能够理解成上图中的每一条线在容器中的位置,每个字都是有本身的高度的,这能够解释为何那些明明是空白的地方为何也属于文字高度计算范围以内。this

在上图中能够看到x被选中时,包含了那些空白的地方,这些其实都是在字体的容器之中的。像这种没有设置特定line-height的文字高度,咱们叫它为内容区高度(the height of the content area)spa

The height of the content area should be based on the font, but this specification does not specify how.

内容区高度是与font-family有着千丝万缕的关系,由于不一样的font-family的有着不同的字体度量,这将会致使font-size相同而font-family不一样的字的内容区高度不同。3d

如上图所示,不一样字体的内容区高度不同。因为不一样字体下设置的字体度量不同,若是都是从内容区顶部开始排列文字,那上图这三个X确定是良莠不齐,因此这些字都是按照基线(baseline)排列的,一个字体容器的基线的位置能够认为是X的底部。上图中三个X的底部都是在一条线上,这是由于它们此时都处于line box的基线上,同时也是由于它们此时的vertical-align属性都是baseline

line-height

不知道你们有没有想过为何设置行高好像能够垂直居中一段文字?
这须要先解释一下line-height值是怎么计算的。在CSS2官方定义文档中中有详细的描述,简单来讲就是将行高的计算值减去内容区高度获得的值,各一半放在内容区上下方(这个计算出来的值能够是负值)。这就能够解释为何设置行高看起来好像是垂直居中。因为文字在内容区中并无垂直居中,所以实际上只设置行高值是不能真正垂直居中一个字的,以下图所示。

这里能够说个尚且在草案中的属性leading-trim,这个属性可使得计算的行高的时候是测量到大写字母高度/字母基线而不是ascender/descender,使得文字在视觉上居中。

有一个问题,行高的默认值到底是多少?在那篇文章中有提到,因为每种字体的设置字体度量不同,所以每一种字体默认行高值都不同,最低的到了0.618,最高甚至到了3.378。

on the 1117 fonts installed on my computer (yes, I installed all fonts from Google Web Fonts), 1059 fonts, around 95%, have a computed line-height greater than 1. Their computed line-height goes from 0.618 to 3.378. You’ve read it well, 3.378!

另外还要说一个很是重要的点,在CSS2官方定义文档中有一个东西strut,文档里是这么说的。

On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut." (The name is inspired by TeX.).

简单理解就是line box里好像有一个0宽度的字(继承父元素属性)存在,strut对于理解那些奇怪的现象相当重要。

vertical-align

要谈vertical-align,不得不先说它几个重点值的定义,vertical-align用很差,很差用的缘由之一就是不清楚它的值具体是什么意思,甚至于有些问题在看完具体的定义以后就能明白为何。

  • baseline:把盒的基线与父级盒的基线对齐。若是该盒没有基线,就把下外边距边界和父级的基线对齐。
  • middle:把该盒的垂直中点与父级盒的基线加上父级的半x-height对齐
  • text-top:把该盒的顶端和父级的内容区的顶端对齐
  • text-bottom:把该盒的底端和父级的内容区的底端对齐
  • top:把对齐子树的顶端与行框的顶端对齐
  • bottom:把对齐子树的底端与行框的底端对齐

middle值中说的半x-height其实能够理解为盒中直接写一个没有标签包裹的x字母的一半高度,这个只受到父盒样式影响的x其实能够看做strut的有形表现,在后面的实验中也是常常将这个用做strut来查看基线的位置以及strut内容区高度的大小。其余的值的定义就说的很清晰,无需做过多解释。
那么说完值以后再说一下vertical-align规定,一样也是看完以后就可以理解一些问题的缘由。

  1. vertical-align的值只对父级行内元素或者一个父级块容器元素的strut有效。
  2. 在上面值的定义中,对于行内非替换元素,用于对齐的盒是那个高度为line-height(包括该盒的内容去和两边的半行距)的盒。对于其它全部元素,用于对齐的盒都是外边距框。
  3. inline-block(盒)的基线是它的最后一个常规流中的行框的基线。
  4. 若是行框没有流内行框或者其overflow属性的计算值不为visible,此时行框的基线是下外边距边界。

第一条只对行内元素有效的规定想必你们都清楚,可是一样对stuct有效这一点,我想知道怎样才能对它设置vertical-align的值,等评论大神指点一下。

第三点我的认为是有问题的,据我所知,设置了vertical-align的元素仍在常规流中,那么第一个问题:假设最后一个常规流中的行框设置了vertical-alignmiddle,此时它根据上述middle值的定义,须要将该盒的垂直中点与父级盒的基线加上父级的半x-height对齐,但若是根据第三条规则所说,此时将产生矛盾。

第二个问题是:假设盒内只有一个行框,但此时此行框的vertical-align属性的值为middle,那么此时父盒的基线在哪?

所以我认为此第三条规则应该是这样:inline-block(盒)的基线是它的最后一个常规流中而且vertical-align属性为baseline的行框的基线,若是盒中的行框的vertical-align属性都不为baseline,那么基线位置由strut肯定。

第四条规定咱们看个图就明白了。

图中灰色线条是我画的基线所在位置,第二个行框内是空的,所以此时它的基线位置是它的下外边距,因此才会形成这样图中这样奇特的状况出现。

另外,行内替换元素的基线位置就须要注意一下。

能够看到图中的imgtextareavideo元素的基线都是下外边距,而selectinput元素的基线都是其中的文字基线。这里又能够看到一个神奇的现象,那就是若是拉扯textarea的右下角,将它向下拉扯,textarea的高度会变大没错,可是因为基线须要与父盒基线对齐,因此实际上textarea是向上增加的。

现象解析

正所谓学以至用,有了上面打下的基础,如今跟着我一块儿来分析那些神奇的现象吧。

1.第一个固然是我遇到的问题:设置相同的行高状况下,对后面那个X设置vertical-alignmiddle,却下移了。

其实这个还挺好理解的,vertical-align属性的middle值中的定义加上第二条规定就能解释了,就是第二个行框高度的中线与第一个行框的基线加上X高度的一半(可看做x的中点)对齐,画个线让大家看的更明白。

因为第二个行框中的x在行框中不是垂直居中的(由于X在内容区中不是居中的),因此最终致使了这样的现象。

2.设置了vertical-align没有效果。

首先第一个条件是行内元素须要知足,若是不知足那确定没有效果。
若是知足了行内元素的要求,那么我认为其实不存在没有效果的状况,一般这种状况是修改值后的位置和修改前的一致,因此看起来好像没有效果,但其实是生效了,我上个图展现某一种无效的状况。

图中第二个行框设置了vertical-alignmiddle,而后第二个行框中的字号不断变化,变化的过程当中能看到,出现了两个基线在同一条线上的状况,看起来好像是在同一条基线上,即就是好像与vertical-alignbaseline的状况一致。所以若是遇到了设置vertical-align后没有变化的状况,静下心来用我上面提供的原理进行分析,确定能找到缘由。

3.行框设置了line-height后,行框实际高度大于line-height

其实这个问题本质和张鑫旭文章中提到的幽灵空白节点是同样的,这类问题的本质就是line box中不可见的strut在做妖,这个strut会继承父盒的样式,像line-heightfont-sizefont-family等等都是能够继承的样式,而正如我所说,这些样式是致使这些现象的关键因素。话很少说,分析一个例子。

如图所示,在外层div设置了line-height:50px,同时给子行框设置font-size:50px,这样却致使了实际高度大于咱们预期的50px,为何就这样呢?是的,就是strut在做妖,因为没有在外层div设置字号,所以strut的字号属性继承的是外层div,此时strut的字号是默认字号,而默认字号的内容区高度比50px的内容区高度小,同时二者的都是基于基线对齐的,根据line-height的计算方式,strut下面分配到高度增长,从而致使下方出现了空白,我写一个不带样式的X来展现一下就明白了。

左边的x浅蓝色部分就是它的内容区高度,而黑色部分则是因为设置了line-height后计算分配获得的高度,注意看上下黑色高度是同样的。看到这里就明白了,引发这些神奇现象的魔力之源就是这个strut以及那四个样式。

那么如何去掉这些空白,因为没法直接操做到strut,那么只能对那四个属性下手了。首先得明白解决此类问题的核心思路:消去无形strut的影响stuct是怎么影响到咱们的呢?那是由于它是有一个具备高度的东西,而且与baseline定位有关。那么具体的解决思路便有二:1.消去它高度的影响。2.消去它定位的问题。

如何消去strut的高度呢?其实这个问题就是如何使一个文字失去高度,这个问题想必你们都清楚,让font-sizeline-height变为0(若是父盒设置了line-height固定值再设置font-size为0,strut仍是有高度的)。

(如下分析皆基于上图分析,且将左边行框看成strut
那么怎么消去它定位的问题呢?固然是靠vertical-align这个属性了,其实网上的文章都有写到设置vertical-align的值能够去掉空白部分,可是都没有仔细说说为何设置完就能消掉,这样就容易出现问题。比方上图的状况,对后面的X设置vertical-alignmiddle并不能消掉空白,你们回想一下middle的定义,想象一下设置以后的效果。结果就是,右边的x下移,空白部分到了上方。由于设置middle值后是右方行框中点去对齐左方X的中点,而左方X的中点是在左方行框中间偏下的地方,因此像这种状况设置middle是消除不掉空白的。

那么你们认为text-top/text-bottom能消掉空白部分吗?再来回想这两个值的定义,若是想的起来话答案固然就脱口而出:不能。由于这两个值是让右边的行框去对齐左边行框内容区的顶部或者底部,即浅蓝色的顶部或者底部。

那么另外两个经常使用的top/bottom能够消掉这些空白吗?答案是能够的,这两个值的定义就是对齐父盒行框中最高或者最低点,因为两个行框的高度一致,因此是能够对齐的。可是呢,若是右边的行框高度小于左边行框的高度,还能消去那空白吗?若是左边行框的高度小于右边行框的高度,表现出来的样式一致吗?(这两个问题但愿读者能够采用我下面的方法来进行猜测并验证)

这里要重点提一个我的观点:行框内全部vertical-align不为baseline的元素一开始不会进入位置的计算,在baseline肯定后,再进入定位,定位后行框从新计算样式。或者这个观点能够做为分析的方法,上述的问题的例子都比较小,若是行框中的元素变多,且各自的垂直定位的值都不同,分析起来就比较困难。可是若是用个人这个观点来分析则比较容易,而且结果也证明此观点无问题。

总结

若是有人能看到这里,但愿你已经明白这些现象的本质所在,而且在之后遇到了这些问题,都能贻笑大方的轻松解决。我在编写这篇文章时查阅了大量资料,可是有不少东西在官方文档里也只是只言片语带过,所以诞生了不少自个人观点,若有错误,欢迎指点。

如下是个人我的观点的总结:

  1. 一般在line box里遇到一些没法理解的现象,一般都与font-family font-size line-height vertical-align四个属性以及strut有关系。
  2. inline-block(盒)的基线是它的最后一个常规流中而且vertical-align属性为baseline的行框的基线。若是盒中的行框的vertical-align属性都不是baseline,那么基线位置由strut肯定。
  3. 行框内全部vertical-align不为baseline的元素一开始不会进入位置的计算,在baseline肯定后,再进入定位,定位后行框从新计算样式。)
  4. 消除这些现象的本质是消除strut的影响以及理解vertical-align属性的值的定义。

(能不用vertical-align就不用吧,flex大法好)

相关文章
相关标签/搜索