Adamwu1992 / adamwu1992.github.io

My Blog
2 stars 0 forks source link

【CSS】行盒子与垂直对齐 #8

Closed Adamwu1992 closed 5 years ago

Adamwu1992 commented 6 years ago

行盒子(line box)

所谓行盒子,就是在block元素中,如果存在inline元素,这些inline元素按照顺序排列,宽度不够时会自动换到下一行,每一行就形成了一个inline box。

每一个inline box的高度是由它包含的子元素决定的,直觉上inline box的高度等于最高子元素的高度,但是这是不对的,inlne box的高度还和子元素的对齐方式有关。

<style>
.inner {
  width: 200px;
  height: 200px;
  border: 1px solid #ccc;    
}
</style>

<div class="wrapper">
  <div class="inner"></div>
</div>

以上代码,在Chrome的测量,inner的高度时202px没有问题,但是外面div的高度是206px,而且可以看见inner底部和父元素之间存在一条缝隙。这条缝隙存在的原因就和inner的对齐方式有关。

垂直对齐(vertical-align)

在CSS中vertical-align可以设置行内元素的在垂直方向上的对齐方式,按照文档描述,vertical-align可以有以下值:

回到上一个问题,显然inner的对齐方式是baseline,为何baseline的对齐方式会导致一条缝隙呢?查看baseline的定义:

Aligns the baseline of the element with the baseline of its parent.

将目标元素的baseline和父元素的baseline对齐。

首先,如何确定一个元素的baseline?

但是,上文的例子中,元素的子元素被指定了对齐方式,那元素的baseline取决什么呢? 首先我们想个办法直观的看到父元素的baseline,给父元素添加伪类:

<style>
. wrapper::after {
  content: "x"
}
</style>

这样可以通过观察x的位置,就可以知道元素的baseline。 在Chrome的元素检查器中查看这个x,可以发现inner的baseline(下边缘)和x的baseline对齐,但是x元素的高度撑开了一个高度造成了父元素比inner更高,从而出现了一条缝隙。 所以可以推测出,如果元素中所有的子元素都制定了对齐方式,那么会默认添加一个空的文本元素,二元素的baseline就是取决于这个文本元素的。

如何去掉这个缝隙呢? 首先可以设置元素的字体大小为0,子元素的字体大小再单独设置,这样这个空的文本元素的高度就是0,也就不会再撑开一个高度了。 还有一个方法就是和这篇文章的主题相关了,既然是inner的对齐方式baseline导致了这条缝隙,我们改变inner的对齐方式试试看。

<style>
.inner {
  vertical-align: sub;
}
</style>

首先设置为sub,为了直观一些在元素里添加一个子元素<sub>sub</sub>,可以看到inner的baseline是和sub的baseline对其的,而x的baseline向上移动了一些。所以sub的位置是基于元素的baseline的,而inner的baseline和sub元素的baseline对齐;

同样的,inner的对齐方式设置为super时,添加一个<sup>sub</sub>观察,发现inner是和sub对齐的,而x的位置向下了一些;

基于这两种情况下的表现,我们发现父元素的baseline位置是可以被改变的。

当我们将inner设置为text-top时(删除sup和sub元素),发现元素的高和子元素相等了,而x出现在元素上方,查阅文档发现,这种对齐方式和baseline无关:

Aligns the top of the element with the top of the parent element's font.

将元素的顶部(margin-top)和父元素文本的顶部对齐,x就上移到了靠近元素顶部的位置,而根据baseline的定义方式,此时父元素的baseline也移动到靠近元素顶部的位置。

text-bottom的表现也是相似的,x移动到了元素靠近底部的位置,父元素的baseline也移动到靠近元素底部的位置,之所以是靠近,是因为x文本元素本身有一个高度比字母x更高。

再试一下middle,发现x出现在元素中间位置,文档这样描述的:

Aligns the middle of the element with the baseline plus half the x-height of the parent.

将元素的中间和父元素的baseline + x的一半高度,看文字很难理解,想象在纸上如何画出这个布局:

  1. 先画一个矩形,宽高为10cm,这就是inner,找出中线,距离顶部5cm;
  2. 因为inner的中线和父元素baseline+x一半对其,给x一个高度1cm,所以baseline的位置就是距离顶部5.5cm的一条线,而x是距离顶部4.5cm,高度1cm的的小矩形;
  3. 父元素的高度是被子元素自然撑开的,两个子元素的inner和x位置大小都确定了,那么父元素的高度也随之确定了,就是10cm;

lengthpercentage两个是相似的,就是在vertical-align: baseline的基础上,偏移指定的距离,其中百分比是根据父元素的额line-height计算出来的,元素的默认行高可以另开一篇。。。

最后是topbottom,文档这样描述他们:

top: Aligns the top of the element and its descendants with the top of the entire line. bottom: Aligns the bottom of the element and its descendants with the bottom of the entire line.

top: 将元素及其后代的顶部和整行的顶部对齐; bottom: 将元素及其后代的顶部和整行的底部对齐。

toptext-top对比一下发现,差异在于整行的顶部和文字的顶部,如果未指定父元素的行高,这两个的表现是相似的,但是当父元素的行高远大于字体所需要的高度时,top可以保证inner始终顶着父元素顶部,而text-top会在顶部出现空白。

总结

所有元素都存在一个baseline,元素的baseline取决于他的子元素(最后一个未指定对齐方式的子元素的baseline),子元素在垂直方向上的对齐方式由vertical-align决定,其中vertical-align所有的值中,baselinesubsuper<length><%>和baseline紧密相关,topbottomtext-toptext-bottom和baseline无关,middle严格意义上和baseline也是无关的,描述上是字体高度的一半加上baseline。

Adamwu1992 commented 5 years ago

有些错误,更新的在#16