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

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

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

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

但还有另外一个角色: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)。它们可以具有width,height(也有可能是通过其内容确定的)和padding,border及margin

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

A tall        in a line of text.    A short        in a line of text.           This                can                happen.    

行盒就是我们的上下文(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是根据它自身的规则来确定的

含有流内内容但具有计算值为非visible的overflow属性时,baseline是margin-box的底边(中间的例子),所以,它与内联-块元素的底边相同

不含流内内容时,baseline也是margin-box的底边(右边的例子)

行盒

    x            This                        can                        happen.            

上图中,把行盒的文本盒(更多信息见下文)的顶边和底边用绿色画出来,而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对齐

        x            baseline             sub             super             -50%             +10px            

baseline:元素的baseline恰好与行盒的baseline重合

sub:元素的baseline移到行盒baseline下方

super:元素的baseline移到行盒的baseline上方

<percentage>:元素的baseline相对行盒的baseline移动关于line-height的百分比

<length>: 元素的baseline相对行盒的baseline移动一个绝对长度

元素的outer edge相对行盒baseline对齐

        x             middle                        

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就不得不移动。因为大多数竖直对齐(除了top和bottom)都是相对其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-align为bottom(左图)和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% wide50% wide… and in next line50% wide50% 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>

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

更多相关文章

  1. Selenium3自动化测试【12】元素定位认知
  2. Jquery对选取到的元素显示指定的长度,对于的字符串用“...”显示
  3. jQuery编程基础精华02(属性、表单过滤器,元素的each,表单选择器,子元
  4. scrollTop到溢出滚动div中的活动元素
  5. jQuery使用之(四)处理页面的表单元素
  6. jQuery遍历祖先元素:parentsUntil
  7. jQuery入门(2) 获取元素和DOM转换
  8. 用Jquery控制元素的上下移动 实现排序功能
  9. jQuery -添加活动类并在单击时从其他元素中删除活动类

随机推荐

  1. Android调用摄像头闪退
  2. Android官方资料--A/B System Updates
  3. c#开发android时layout.axml没有智能提示
  4. 解读ExpandableListView源码样式
  5. 初探 Ubuntu下android NDK
  6. 在 actionbar中使用 searchview
  7. Android Studio点击按钮更换背景图片
  8. Android 常用权限
  9. android相关
  10. android scroller类的使用