为什么要写这篇文章一切都始于一个百思不得其解的问题。首先,由于我的错误认识,我认为只要在一行中设置line-height,这一行的文字就会居中显示。如果字号相同,这个效果是正常的,但是如果一行中出现字号不同的字,情况就不一样了。第二个X不是垂直居中的。这时候我想到了vertical-align的公式,尝试为后面的X设置vertical-align:middle,于是神奇的事情发生了,后面的字符没有按预期向上移动,而是稍微向下移动了一点.什么???有这么神奇吗?造成这种效果的魔法是什么?为此,我查阅了很多资料,终于明白了造成这种现象的本质原因,但是我查阅的很多资料都或多或少不完整,难以理解,甚至有些错误,误导了大家。因此,我决定结合自己的思路,写一篇通俗易懂的文章,全面剖析vertical-align这个神奇现象的本质。原则是授人以鱼不如授人以渔。只有了解了一个现象的本质,才能在以后遇到类似的问题时不至于一头雾水。仔细考虑后,我对这种现象得出了一个结论:我通常会在linebox中遇到一些无法理解的现象,这些现象通常与font-familyfont-sizeline-heightvertical-align这四个属性有关。可以说这四个属性是解释lineboxes很多难以理解的性能的关键。这个问题在深入研究CSS:字体规格、行高和垂直对齐一文中有详细解释。这篇文章写得非常好,强烈建议您再读一遍。本文部分知识点来源于此文。文中详细描述的部分将在本文中以我理解的方式进行解释。那么我们就来看看这四个属性的相关原理吧。字体和字体大小每个单词都被一个容器(em-square)包裹起来,并且在这个容器中定义了字体度量,例如ascender、descender、capitalheight和x-height。这些字体指标可以理解为上图中每一行在容器中的位置,每个字符都有自己的高度,这就可以解释为什么那些明显空白的地方也在文本高度计算范围内。Inthepictureabove,youcanseethatwhenxisselected,itincludesthoseblankplaces,whichareactuallyinthefontcontainer.我们称这个没有特定行高设置的文本高度为内容区域的高度。内容区域的高度应基于字体,但本规范未指定如何。内容区的高度与font-family密切相关,因为不同的font-family有不同的fontmetrics,这会导致font-size相同但font-family不同的词,内容区的高度不同。如上图所示,不同字体的内容区域高度不同。由于不同字体下设置的fontmetrics不一样,如果文字是从contentarea的顶部开始排列的,上图中的三个X肯定是参差不齐的,所以这些文字是按照baseline(基线)排列的,一个字体容器基线的位置可以认为是X的底部,上图中三个X的底部都在同一条线上,因为此时它们都在linebox的基线上,并且也是因为此时他们的vertical-align属性都是baselines。line-height不知道大家有没有想过,为什么设置行高好像是让一段文字垂直居中?这就需要解释一下line-height值是怎么计算的了。CSS2官方定义文档中有详细说明。简单的说,就是把行高的计算值减去内容区的高度得到的值,分别在内容区上下各放一半(这个计算值可以是负数)。这可以解释为什么设置行高似乎使其垂直居中。由于文本在内容区域没有垂直居中,实际上只设置行高值并不能真正垂直居中一个词,如下图所示。这里我们可以谈谈还在草稿中的属性leading-trim。该属性可以使行高的计算测量大写字母高度/字母基线而不是ascender/descender,从而使文本在视觉上居中。一个问题,行高的默认值是多少?在那篇文章中提到,由于每种字体的字体规格不同,所以每种字体的默认行高值都不一样,最低的是0.618,最高的甚至是3.378。在我电脑上安装的1117种字体中(是的,我安装了所有来自GoogleWebFonts的字体),1059种字体(大约95%)的计算行高大于1。它们的计算行高从0.618到3.378。你读得很好,3.378!还有一个很重要的一点就是CSS2官方定义文档中有一个strut,就是文档里面说的。在内容由行内级元素组成的块容器元素上,'line-height'指定元素内行框的最小高度。最小高度由基线上方的最小高度和基线下方的最小深度组成,就好像每个行框都以具有元素的字体和行高属性的零宽度内联框开始。我们称那个假想的盒子为“支柱”。(这个名字的灵感来自于TeX。)。简单理解就是好像有一个0字宽的字(继承父元素属性)存在,strut对于理解那些奇怪的现象至关重要。vertical-align要说vertical-align,就不得不说说几个键值的定义。vertical-align不好用。不好用的原因之一就是不清楚它的值是什么意思,甚至有些问题看了具体的定义就明白了。baseline:将框的基线与父框的基线对齐。如果框没有基线,则将底部边距边框与父级的基线对齐。middle:将框的垂直中点与父框的基线加上父框x高度的一半对齐text-top:将框的顶部与父内容区域的顶部对齐text-bottom:将框对齐Alignthebottomofparentwiththebottomofthecontentareaof??theparenttop:Alignthetopofthesubtreewiththetopofthelineboxbottom:将子树的底部与行框的底部对齐halfx中间的值-height其实可以理解为没有标签直接包裹在盒子里的字母x高度的一半。这个只受parentbox样式影响的x,其实也算是strut的一种有形体现。这在以后的实验中经常被用作支柱。strut查看基线的位置和strut内容区域高度的大小。其他值的定义很清楚,不做过多解释。那么说完了值,再来说说vertical-align的规定。这也是为什么有些问题看了之后就能明白的原因。vertical-align的值仅对父内联元素或父块容器元素的struts有效。在上述值的定义中,对于行内非替换元素,用于对齐的框是高度为line-height的框(包括框的内容和两边的半行间距)。对于所有其他元素,用于对齐的框是边距框。内联块(框)的基线是其最后一个常规流行框的基线。如果行盒没有流内联盒或者其overflow属性的计算值不可见,那么行盒的基线就是下边距边界。第一条只对内联元素有效的规则想必大家都很清楚,但是对stuct同样有效。想知道怎么给它设置vertical-align的值,等评论高手指教。第三点我个人认为是有问题的。据我所知设置了vertical-align的元素还在正常流中,所以第一个问题:假设正常流中最后一个linebox设置了vertical-align为middle,此时根据上面中间值的定义,盒子的垂直中点需要和父盒子的基线加上父盒子的一半x-height对齐,但是如果按照第三条规则说,就会产生矛盾。第二个问题是:假设盒子里只有一个linebox,但是此时这个linebox的vertical-align属性的值为middle,那么此时parentbox的baseline在哪里呢?所以我觉得这第三条规则应该是这样的:inline-block(box)的基线是linebox在它最后一个正常流中的基线,vertical-align属性是baseline,如果vertical-align属性是lineboxintheboxisvertical-如果align属性不是baseline,那么baseline位置由strut决定。第四条规定我们看一张图就可以理解。图中灰线就是我画的baseline的位置,而第二个linebox是空的,所以此时它的baseline位置就是它的下边距,这就是为什么会出现图中这样奇怪的情况。另外需要注意被替换元素在行中的基线位置。可以看出图中img、textarea、video元素的基线都是下边距,而select、input元素的基线是文本基线。这里可以看到另一个神奇的现象,就是如果把textarea的右下角往下拉,textarea的高度会变大,但是因为baseline需要和父框的baseline对齐,所以textarea实际上在增长。现象分析就是所谓的学以致用。有了上面的基础,现在就跟着我来分析一下那些神奇的现象吧。1、第一个当然是我遇到的问题:在设置相同行高的情况下,对后面的X设置vertical-align为middle,但是却下移了。其实这很容易理解。vertical-align属性中间值的定义加上第二条规则就可以解释了,即第二行框高度和第一行框基线的中线加上X高度的一半对齐(可以看成x的中点),画一条线,这样大家看得更清楚。这最终会发生,因为第二个行框中的x没有在行框中垂直居中(因为X没有在内容区域中居中)。2.设置vertical-align没有效果。首先,第一个条件是内联元素需要满足。如果不满足,肯定是没有效果的。如果满足内联元素的要求,那我觉得也不是没有效果。通常,修改值后的位置和修改前的位置是一样的,所以看起来没有效果,但实际上是有效果的。I上图显示了无效的情况。图中第二行框中设置了vertical-align为middle,然后第二行框中的字体大小不断变化。在变化过程中,可以看到两条基线在同一条线上。看起来好像是在同一个baseline上,也就是好像以vertical-align为baseline是一致的。所以,如果遇到设置vertical-align后没有变化的情况,静下心来用我上面提供的原理来分析一下,一定能找到原因。3、line-box设置line-height后,line-box的实际高度大于line-height值。其实这个问题的本质和张新旭文章中提到的ghostblanknode是一样的。这类问题的实质是Strut在充当恶魔的linebox中不可见,这个strut会继承parentbox的样式,比如line-height、font-size和font-family等。是可继承的风格,正如我所说,这些风格是导致这些现象的关键因素。话不多说,我们来分析一个例子。如图,在外层div设置了line-height:50px,同时给子行框设置了font-size:50px,但是实际高度却大于我们预期的50px,为什么这样吗没错,strut就是在作妖。由于外层div中没有??设置字号,所以strut的字号属性继承自外层div。此时strut的字体大小为默认字体大小,默认字体大小的内容区域高度比50px的内容区域高度小,两者对齐基于基线。根据line-height的计算方式,分配给strut底部的高度会增加,导致下方出现空白。我会写一个没有风格的X来展示它。.左边x的浅蓝色部分是其内容区域的高度,黑色部分是设置line-height后计算分配的高度。请注意,上下黑色高度相同。看到这里就明白了,造成这些奇异现象的魔力源头,就是这大腿和那四式。那么如何去除这些空白呢,既然不能直接操作strut,那我们只能从那四个属性入手了。首先,我们必须了解解决此类问题的核心思想:消除隐形支柱的影响。结构如何影响我们?那是因为它有一个高度,和基线定位有关。那么具体的解决方案有两种:1.消除其高度的影响。2.消除其定位问题。如何消除支柱的高度?其实这个问题就是如何让一个文字失去高度。这个问题想必大家都很清楚。让font-size或者line-height变为0(如果parentbox设置了一个固定的line-height值,然后将font-size设置为0,strut还是withheight)。(下面的分析是在上面的分析基础上,把左边的linebox当做strut)那么如何排除它定位的问题呢?当然,这取决于属性vertical-align。其实网上有文章写过设置vertical-align的值可以去掉空白部分,但是没有详细说明为什么设置后可以去掉,容易出问题。比如上图中,把后面的X设置vertical-align为middle,并没有消除空白。让我们回忆一下middle的定义,想象一下设置后的效果。结果是右边的x向下移动,空白部分向上移动。因为设置了中间值后,右边线框的中点和左边X的中点对齐,而左边X的中点在左边线框中间的下方,所以在这个设置中间案件并未消除。放下空白的。那你觉得text-top/text-bottom能去掉空白部分吗?回想一下这两个值的定义。仔细想想,答案当然会脱口而出:没有。因为这两个值是为了让右边的行框和左边行框的内容区域的顶部或者底部对齐,也就是浅蓝色的顶部或者底部。那么另外两个常用的top/bottom能不能消除这些空白呢?答案是肯定的,这两个值的定义是对齐parentbox的linebox中的最高点或最低点。由于两个lineboxes的高度相同,所以可以对齐。但是,如果右边linebox的高度小于左边linebox的高度,是否可以消除空白呢?如果左边行框的高度小于右边行框的高度,显示的样式是否一致?(希望读者可以用我下面的方法来猜测和验证这两个问题)这里我想强调一个个人的观点:lineframe中vertical-align不是baseline的所有元素都不会在开始。确定后再次输入定位,linebox会重新计算定位后的样式。或者这个观点可以作为一种分析方法。上述问题的例子比较少。如果linebox中的元素很多,它们的垂直定位值不一样,分析起来会比较困难。但是用我的观点更容易分析,结果也证实了这个观点没有问题。总结如果有人看到这里,希望你已经了解了这些现象的本质,以后可以轻松解决这些问题。写这篇文章的时候查阅了很多资料,但是有很多东西在官方文档中只是三言两语提到过,所以就诞生了很多自己的看法。如有错误,请指出。以下是我个人观点的总结:通常在linebox中遇到一些难以理解的现象,通常与font-familyfont-sizeline-heightvertical-align和strut这四个属性有关。内联块(框)的基线是其在正常流中具有垂直对齐属性基线的最后一个行框的基线。如果box中的lineboxes的vertical-align属性都不是baseline,那么baseline位置由strut决定。所有lineframe中vertical-align不是baseline的元素一开始不会进入position计算,待baseline确定后才进入定位,lineframe会在定位后重新计算样式。)消除这些现象的本质是消除strut的影响,理解vertical-align属性值的定义。(如果能用vertical-align就别用,flex是个好办法)
