前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vertical-align刨根问底

vertical-align刨根问底

作者头像
ayqy贾杰
发布2019-06-12 12:36:51
1.2K0
发布2019-06-12 12:36:51
举报
文章被收录于专栏:黯羽轻扬黯羽轻扬黯羽轻扬

写在前面

本文第一部分翻译自Vertical-Align: All You Need To Know,就是之前在CSS上下左右居中参考资料部分提到的待翻译的那一篇

其余部分是对原文的技巧总结

一.译文

经常需要让一些并排显示的元素竖直对齐

CSS提供了一些可选方案,有时通过float来解决,有时用position: absolute,有时甚至用手动添加marginpadding这样的脏方法,我不很喜欢这些方案。浮动只是让它们顶部对齐,而且要手动清除(浮动的影响)。绝对定位让一些元素脱离标准文档流,以至于它们无法再影响周围元素。而即使是最微小的变动也会破坏固定marginpadding

但还有另外一个角色:vertical-align。我觉得它更值得信任。虽然在技术上,用vertical-align实现布局是一种hack,因为它不是为布局设计的,而是用来对齐文本与文本旁边元素的。但是,也能用vertical-align在不同环境中灵活且细粒度(fine-grained)地对齐元素。不需要知道元素的大小,元素仍然处于标准文档流中,其它元素能响应其尺寸变化。这些优势让它成了一个有价值的选项

vertical-align的怪脾气

vertical-align有时候真的很讨厌,用起来会有些挫败感,似乎有一些神秘的规则。例如,可能会遇到,改变元素的vertical-align根本没有改变它自己的对齐方式,但同一行的其它元素(的对齐方式)却变了!现在还时不时地钻进这些阴暗的角落,让我抓狂(tearing my hair)

不幸的是,大多数相关资源都太浅显了,尤其是在我们想用vertical-align实现布局时。他们专注于试图让一个元素里面的所有东西都竖直对齐的错误想法,给出属性的基本介绍,并解释非常简单的场景下元素的对齐方式,而不解释技巧性的部分

所以,我给自己定下了一劳永逸地澄清vertical-align行为的目标,以深入W3C的CSS规范,并尝试一些例子告终,最终成果就是本文

那么,下面我们从游戏规则入手

vertical-align的依赖项

vertical-align用来对齐内联级(inline-level)元素,也就是那些display属性的计算值为:

  • inline
  • inline-block
  • inline-table(本文不考虑)

内联元素(inline elements)是基本标签包裹着的文本

内联-块元素(inline-block elements)就像它名字所说的那样:内嵌的块元素(block elements living inline)。它们可以具有widthheight(也有可能是通过其内容确定的)和paddingbordermargin

内联级元素(inline-level elements)在一行中一个挨一个地排列,一旦当前行放不下了,就在它下方创建一个新行,所有这些行都具有所谓的行盒(line box),包住这一行的所有内容。不同大小的的内容意味着不等高的行盒。下图中行盒的上下边界用红线标出来了:

A tall in a line of text.

A short in a line of text.

Thiscanhappen.

行盒就是我们的上下文(the line boxes trace out the field we are playing on),这些行盒中的vertical-align属性负责对齐各个元素。那么,元素对齐到底是怎么回事?

baseline和outer edge

竖直对齐最重要的参照点是相关元素的baseline,某些情况下,元素包裹盒的顶边和底边也很重要。我们一起看看各种类型元素的baseline和outer edge在哪里:

内联元素

aAÄ qQ

aAÄ qQ

aAÄ qQ

可以看到3行并列的文本,行高的顶边和底边用红线表示出来,字体的高度用绿线,baseline用蓝线。左边文本的行高设置为与font-size相同,绿线和红线重合了。中间文本行高是font-size的2倍。右边行高是font-size的一半

内联元素的outer edge与其行高的顶边和底边对齐,如果行高小于字体高度的话,就无所谓。所以,outer edge是上图中的红线

内联元素的baseline是字符坐在上面的那条线(baseline is the line, the characters are sitting on),即图中的蓝线。很难理解的是,baseline有时会在字体高度的下方,见W3C规范的详细定义

内联-块元素

c

c

从左到右依次是:含有流内(in-flow)内容(那个“c”)的内联-块元素,含有流内内容和overflow: hidden的内联-块元素和不含流内内容(但内容区具有高度)的内联-块元素。margin的边界用红线表示出来,border为黄色,padding为绿色,内容区为蓝色,每个内联-块元素的baseline用蓝线表示

内联-块元素的outer edge是其margin-box的顶边和底边,也就是图中的红线

内联-块元素的baseline取决于元素是否含有流内内容:

  • 含有流内内容时,内联-块元素的baseline是常规流中最后一个内容元素的baseline(左边的例子),最后一个元素的baseline是根据它自身的规则来确定的
  • 含有流内内容但具有计算值为非visibleoverflow属性时,baseline是margin-box的底边(中间的例子),所以,它与内联-块元素的底边相同
  • 不含流内内容时,baseline也是margin-box的底边(右边的例子)
行盒

xThiscanhappen.

上图中,把行盒的文本盒(更多信息见下文)的顶边和底边用绿色画出来,而baseline还用蓝线,还给文本元素设置了灰色背景高亮标记出来

行盒的顶边与该行最高元素的顶边对齐,并且底边与该行最低元素的底边对齐,就是上图中用红线表示的部分

行盒的baseline是可变的:

CSS 2.1 does not define the position of the line box’s baseline. — the W3C Specs

这可能是用vertical-align时最让人迷惑的部分了。也就是说,baseline具体放在哪里要满足所有其它条件,比如vertical-align和让行盒高度最小,它是方程中的一个自由参数

因为行盒的baseline是不可见的,无法直观地看出来它在哪里。但很容易就能让他变得可见,只需要在有疑问的行首添一个字符,就像图中添的“x”。如果这个字符没有以任何方式对齐,它默认将坐在baseline上

在baseline周围,行盒含有我们称之为文本盒(text box)的东西。文本盒可以简单地看做一个没有任何对齐方式的行盒中的内联元素。其高度等于其父元素的font-size。因此,文本盒只会包裹行盒中没被格式化过的文本,上图中用绿线表示出来了。因为这个文本盒与baseline绑在一起,baseline动的时候它也跟着动(注:这个文本盒在W3C规范中被称为strut)

这就是最难的部分了。现在,我们已经知根知底了。快速总结一下最重要的几点:

  • 有个区域叫行盒,是竖直对齐发生的地方。它具有baseline,文本盒及顶边底边
  • 内联级元素,是哪些被对齐的东西,它们具有baseline和顶边底边

vertical-align的值

通过使用vertical-align来对上面提到的参照点和内联级元素设定某些关联

元素的baseline相对行盒baseline对齐

xbaselinesubsuper-50%+10px

  • baseline:元素的baseline恰好与行盒的baseline重合
  • sub:元素的baseline移到行盒baseline下方
  • super:元素的baseline移到行盒的baseline上方
  • <percentage>:元素的baseline相对行盒的baseline移动关于line-height的百分比
  • <length>: 元素的baseline相对行盒的baseline移动一个绝对长度
元素的outer edge相对行盒baseline对齐

xmiddle

  • middle:元素顶边底边之间的中点与行盒的baseline加上半个x-height对齐
元素的outer edge相对行盒的文本盒对齐

x text-top text-bottom

  • text-top:元素的顶边与行盒的文本盒的顶边对齐
  • text-bottom:元素的底边与行盒的文本盒的底边对齐
元素的outer edge相对行盒的outer edge对齐

x top bottom

  • top:元素的顶边与行盒的顶边对齐
  • bottom:元素的底边与行盒的底边对齐

当然,正式的定义在W3C规范里都能找到

为什么vertical-align的行为是这样

我们可以更近一步看看某些场景下的竖直对齐,尤其是我们将那些可能出错的场景

居中小图标

有个烦扰着我的问题:我有一个小图标,想要与旁边的一行文本居中对齐。只给小图标来个vertical-align: middle看起来居中效果不那么让人满意。看看这个例子:

Centered?

Centered!

<!-- left mark-up -->
<span class="icon middle"></span>
Centered?<!-- right mark-up -->
<span class="icon middle"></span>
<span class="middle">Centered!</span><style type="text/css">
 .icon   { display: inline-block;
           /* size, color, etc. */ } .middle { vertical-align: middle; }
</style>

这儿还有个相同的例子,但我画出了一些你已经从上面了解到的辅助线:

x Centered?

x Centered!

这样能揭示一些线索,因为左边的文本没有任何对齐方式,它坐在baseline上。实际上,设置vertical-align: middle来对齐小方块,我们把它对齐到了不具上伸部(ascender)的小写字母的中心位置(半个x-height)。所以,具有上伸部的字符显得比较靠上

右边的话,我们让整个字体区的中点也竖直对齐,把文本的baseline相对行盒baseline稍微下移来实现效果。结果是文本和紧挨着的小图标漂亮地居中了

行盒baseline的移动

这是个用vertical-align的常见陷阱:行盒的baseline受该行所有元素的影响。我们假设有个元素以这种方式对齐(相对自身baseline对齐),行盒的baseline就不得不移动。因为大多数竖直对齐(除了topbottom)都是相对其baseline的,导致该行所有其它元素也都跟着调整位置

一些示例:

  • 如果一行有个高元素横跨整个高度,vertical-align对它就不起作用了,它顶部之上和底部之下已经没有能供它移动的空间了。为了满足其相对行盒baseline的对齐关系,行盒baseline就不得不移动了。矮方块具有vertical-align: baseline,左边,高方块是text-bottom对齐,右边是text-top对齐,可以发现baseline带着矮盒子一起跳上去了
  <!-- left mark-up -->
 <span class="tall-box text-bottom"></span>
 <span class="short-box"></span> <!-- right mark-up -->
 <span class="tall-box text-top"></span>
 <span class="short-box"></span> <style type="text/css">
   .tall-box,
   .short-box   { display: inline-block;
                 /* size, color, etc. */ }   .text-bottom { vertical-align: text-bottom; }
   .text-top    { vertical-align: text-top; }
 </style>

在用其它vertical-align值对齐一个高元素时会出现同样的行为

  • 甚至设置vertical-alignbottom(左图)和top(右图)也会移动baseline,这就怪了,因为根本不牵扯baseline啊
  <!-- left mark-up -->
 <span class="tall-box bottom"></span>
 <span class="short-box"></span> <!-- right mark-up -->
 <span class="tall-box top"></span>
 <span class="short-box"></span> <style type="text/css">
   .tall-box,
   .short-box { display: inline-block;
                /* size, color, etc. */ }   .bottom    { vertical-align: bottom; }
   .top       { vertical-align: top; }
 </style>
  • 一行里放两个大元素,竖直对齐它们会移动baseline到满足它们对齐方式的位置,然后行盒的高度也会调整(左图)。添上第三个元素,其对齐方式不会让它超出行盒的边界的话,既不影响行盒的高度也不影响baseline的位置(中图)。如果它超出了行盒的边界,行盒的高度和baseline就会再次调整,这种情况下,我们最初的两个方块被推下去了(右图)
  <!-- left mark-up -->
 <span class="tall-box text-bottom"></span>
 <span class="tall-box text-top"></span> <!-- mark-up in the middle -->
 <span class="tall-box text-bottom"></span>
 <span class="tall-box text-top"></span>
 <span class="tall-box middle"></span> <!-- right mark-up -->
 <span class="tall-box text-bottom"></span>
 <span class="tall-box text-top"></span>
 <span class="tall-box text-100up"></span> <style type="text/css">
   .tall-box    { display: inline-block;
                  /* size, color, etc. */ }   .middle      { vertical-align: middle; }
   .text-top    { vertical-align: text-top; }
   .text-bottom { vertical-align: text-bottom; }
   .text-100up  { vertical-align: 100%; }
 </style>
内联级元素下方可能会有小间隙

看看这种情况,试图vertical-align列表里的li时,很容易遇到:

<ul>
 <li class="box"></li>
 <li class="box"></li>
 <li class="box"></li>
</ul><style type="text/css">
 .box { display: inline-block;
        /* size, color, etc. */ }
</style>

如图所示,列表项坐在baseline上,baseline下方是一些用于来容纳文本下延部(descender)的空间,造成了间隙。解决方案呢?只需要把baseline移远一点,例如,用vertical-align: middle对齐列表项:

<ul>
 <li class="box middle"></li>
 <li class="box middle"></li>
 <li class="box middle"></li>
</ul><style type="text/css">
 .box    { display: inline-block;
           /* size, color, etc. */ } .middle { vertical-align: middle; }
</style>

这种场景不会出现在含有文本内容的内联-块元素中,因为内容已经移到baseline上了

内联级元素之间的间隙破坏布局

这主要是内联级元素自身的问题,但因为它们是vertical-align的依赖项之一,所以最好了解清楚

在前一个例子中也能看到列表项之间的间隙,间隙来自出现在标记代码(HTML/XML等)里的内联元素之间的空白字符。内联元素之间的所有空白字符都被合并成一个空格,就是这个空格碍事,例如想让两个内联元素仅挨在一起并都设置width: 50%的话,就没有足够的空间容纳两个50%的元素和一个空格。所以会拆分成2行破坏布局(左图)。为了去掉间隙,我们需要去掉空白字符,例如用HTML注释(右图)

50% wide

50% wide… and in next line

50% wide

50% wide

<!-- left mark-up -->
<div class="half">50% wide</div>
<div class="half">50% wide... and in next line</div><!-- right mark-up -->
  <div class="half">50% wide</div><!--
--><div class="half">50% wide</div><style type="text/css">
 .half { display: inline-block;
         width: 50%; }
</style>

vertical-align揭秘

嗯,就是这样,一旦知道规则后就不很复杂。如果vertical-align不生效,只用考虑这些问题:

  • 行盒的baseline和顶边底边在哪里?
  • 内联级元素的baseline和顶边底边在哪里?

这将揭示问题的解决方案

二.技巧

1.怎样确定行盒的baseline?

给这一行加个没有下延部的字符,一般习惯加x,字符的底部边缘就是行盒baseline的位置

例如:

.baseline:before {
   content: 'x';
}

2.怎么确定行盒的边界?

利用上面提到的“元素的outer edge相对行盒的outer edge对齐”:

.line-box-top {
   border-top: 1px dotted red;
   /* 让 border-top 与行盒的顶边重合 */
   vertical-align: top;   /* 宽度沾满整行 */
   display: inline-block;
   width: 100%;
   /* 让开空间,避免影响内容布局 */
   margin-right: -100%;
   /* 提升z,避免被内容遮住 */
   position: relative;
   z-index: 10;
}
/* .line-box-bottom 与之类似 */

在想要明确行盒边界的那一行的行首(因为用margin-right: -100%,所以放最左边)添上

<span class="line-box-top"></span><span class="line-box-top"></span>

即可

3.怎么确定文本盒的边界?

与确定行盒边界的方法类似,利用vertical-align: text-top;vertical-align: text-bottom;

相对谁对齐,那么就能把这个“谁”画出来

4.用HTML注释去掉空白字符技巧

例如:

<figure>
   <span class="large font">
       <span class="green dotted line text-top"> </span><!--
    --><span class="green dotted line text-bottom"> </span><!--
    --><span class="red dotted line top"> </span><!--
    --><span class="red dotted line bottom"> </span><!--
    --><span class="blue dotted line baseline"> </span><!--
    --><span class="font color-grey inline-overlay">x</span><!--
    --><span class="center">
           <span class="middle bg-grey">This</span>
           <span class="tall box bg-grey text-top"> </span>
           <span class="top bg-grey">can</span>
           <span class="tall box bg-grey text-bottom"> </span>
           <span class="bottom bg-grey">happen.</span>
       </span>
   </span>
</figure>

去掉空白字符的同时,保留标签缩进格式,很有意思

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端向后 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 一.译文
    • vertical-align的怪脾气
      • vertical-align的依赖项
        • baseline和outer edge
          • 内联元素
          • 内联-块元素
          • 行盒
        • vertical-align的值
          • 元素的baseline相对行盒baseline对齐
          • 元素的outer edge相对行盒baseline对齐
          • 元素的outer edge相对行盒的文本盒对齐
          • 元素的outer edge相对行盒的outer edge对齐
        • 为什么vertical-align的行为是这样
          • 居中小图标
          • 行盒baseline的移动
          • 内联级元素下方可能会有小间隙
          • 内联级元素之间的间隙破坏布局
        • vertical-align揭秘
        • 二.技巧
          • 1.怎样确定行盒的baseline?
            • 2.怎么确定行盒的边界?
              • 3.怎么确定文本盒的边界?
                • 4.用HTML注释去掉空白字符技巧
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档