inline, inline-block元素在同行元素的排版布局中非常有用,但是时常会出现一些莫名奇妙的问题。要解决这些问题,深刻理解inline,inline-block元素的特征有非常重要的意义。
下面这篇英文文章解释的非常清晰,我视图把他翻译一下,同时也好好学习梳理一下。
http://christopheraue.net/2014/03/05/vertical-align/
我常常需要side by side地垂直方向对齐元素。为了实现垂直对齐,时而我使用float来实现,时而又实用postion:absolue来解决,甚至有时还手工地添加一些margin/padding来完成。实际上我并不喜欢这些解决方案。Floats仅仅在顶部top做了对齐,并且需要手工clear float. absolute定位将元素彻底移出文档流,所以他们再也不会影响到包裹它的元素布局。而第三种手工设置margin/padding的方案往往会在丁点的改动时破坏整个布局!
但是还有另外一种方案: vertical-align. 我想这个属性值得深入研究.通常从技术上来说使用vertical-align来做layout属于hack,因为这个属性本身并不是用来做layout而发明的。vertical-align属性发明的本意是要将紧挨的元素对齐而使用的。然而,尽管如此,你也可以在不同的场景下使用这个属性来细粒度并且灵活地实现你要的对齐效果。更牛的是,元素本身的大小完全可以不用关心。元素由于存在于文档流中,所以其他元素可以针对这些元素尺寸的变化而变化。也正是因为这,使得vertical-align来实现对齐是一个非常有价值的option.
Vertical-align的特点
vertical-align属性对于很多人来说有时甚至是非常糟糕的,使用起来令人沮丧。看起来好像有一些神秘的规则在工作着。比如:有可能你更改了vertical-align属性的那个元素并未有任何改变,而其他的同行元素却神奇的变化了!本文试图揭开vertical-align的面纱让我们彻底掌握它
需要使用vertical-align属性的场景
vertical-align属性用于对齐inline-level elements,也就是具有下列display属性值的元素:
- inline
- inline-block
- inline-table
inline elements基本上就是包裹text的tag。
inline-block元素正如他们的名字所提示的意思:living inline的block元素。他们可以有width和height(很有可能是由它的内容来决定的),也可以有padding,border和margin
inline-level元素在布局时,在行内一个接着一个的排版。一旦元素无法在本行放置的话,就在下面另起一行。所有这些line有几个术语:line box(红色),这个line box会包裹所有行的内容,text box(绿色). baseline: 蓝色.不同大小尺寸的内容意味着不同高度的Line box.下面的延时中,top和bottom由红线来表示
在这些line box里vertical-align属性负责垂直对齐各个元素
About Baselines and Outer Edges
垂直对齐最重要的参考点是相关元素的baseline。在有的情况下,元素的包裹盒子的顶端和低端边缘也会变得非常重要。让我们看看baseline和outer edge对于每一个inline元素在什么位置
这里你可以看到三行紧靠在一起的文本。line height(line box)的top和bottom edge由红线表示,而字体font本身的高度边界则由绿线来表示,本行的baseline由蓝色的线来表示。在最左面,文本的line height和字体的高度设置为相同。这时绿线和红线在高低两端都重合在一起了。而在中间的那个line box,line height是font height的两倍。而在右面的line box,line height则是font-size的一半。
inline元素的outer edge和它line height的top/bottom edge是相匹配的。如果line height本身小于font的height,这没有关系!因此在第三幅红色的line height依然是outer edge.
inline元素的baseline是字符垂直放置的基础,也就是说baseline is the line the characters are sitting on.这就是蓝色的line。可以初略地说,baseline是低于font height(text box)一半的某个位置。
inline-block元素
上面的图中,从左到右你可以看到:一个有着内容c(in-flow content)的inline-block元素,一个有着in-flow content c的同时有着overflow:hidden样式的inline-block元素,和一个没有任何in-flow content的inline-block元素(但是content area有一个高度). margin的边界由红色的line表示,border则用黄色,padding用绿色,content则用蓝色。每个inline-block元素的baseline则用蓝色line表示。
inline-block元素的outer edge是它的margin-box的top/bottom edge.这就是上图中的红线。
inline-block元素的baseline则依赖于它是否有in-flow content:
如果有in-flow content,则元素的baseline就是最后一个content element in normal flow的baseline(就是最左面的图指示的情况)。这个案例中的last element,它的baseline由它自己的rules来决定;
如果有in-flow content,同时其overflow属性设置为非visible,则baseline就成了bottom edge of the margin-box。这是中间那幅图展示的情况。所以baseline就和inline-block元素本身的bottom edge相重合
如果没有内容,则baseline也成为margin-box的bottom edge了,如最右面的图展示的。
你在前面已经看到过上面这张图的类似设置(红色为lineheigh,绿色为font-height,蓝色为baseline)。这次我画了几条线:
1. 红色: line box top/bottom edge(line height定义的)
2. 绿色: text box( font height来定义的)
3. 蓝色: baseline(记住:baseline永远在text box中间,也就是font height一半更低一点的地方)
在上图中, linebox的top edge和本行的top-most element的top edge对齐,而linebox的bottom edge则和本行的bottom-most element的bottom edge所对齐,也就是红线所标识出来的区域
line box的baseline则是变化的:这可能是在使用vertical-align属性时最容易混淆的地方。这意味着,baseline放在能够满足所有其他条件(比如vertical-align和最小化linebox的height)的地方,它就像是等式中的自由变量。
由于line box的baseline并不可见,有时并不会很明显地看到baseline在什么地方。但是,也可以使用一个小小的技巧使得这个baseline遁形:在你不知道baseline在什么地方的那个line-box前面增加一个字符,比如我的例子中,就增加了一个x,如果x没有被align,则默认情况下x就将sit on the baseline default.
在baseline四周, linebox有着我们可以称为linebox的text box存在。text box可以简单地被认为是在line box中没有任何alignment的inline element.它的高度就等于它的parent element的font-size.这样,text box仅仅包裹了未经格式化的line box的text.在上图中,我们就以绿色的线来标识。由于这个text box和baseline是绑定在一起的,因此这个text box也就随着baseline的变化而变化。(side note:这个text box在w3c spec中被成为strut. 这,就是最难理解的地方。现在,我们来将已经了解到的知识再浓缩一下:
1. 有一个被称作line box的区域,这就是vertical alignment要发生的地点。它有一个baseline,一个text box和一个top/bottom edge
2. 有inline-level elements.这些元素就是要被对齐的对象。元素本身也有一个baseline和一个top/bottom edge.
vertical-align属性的值
通过设置vertical-align为不同的值,上面提到的reference point就将会有不同的relationship
aligning the element's baseline relative to the line box's baseline
1. baseline: element的baseline sits exactly on top of the line box's baseline
2. sub: element的baseline移动到linebox的baseline的下方;
3.supper: element的baseline移动到line box的baseline上方
4. percentage: element的baseline相对于line box的baseline移动到相对于line-height的百分比的位置;
5. length: element的baseline被移动到相对于Linebox的baseline一个绝对长度的地方
Aligning the element's outer edges relative to the line box's baseline
1. middle: element的top/bottom edge的中间点被aligned到line box的baseline+x-height的一半的地方;
Aligning the Element’s Outer Edges Relative To the Line Box’s Text Box
你也可以相对于line box的baseline来排列,因为text box的位置是由baseline来决定的:
1. text-top: element的top edge aligned to the line box's text box top edge
2. text-bottom: element的bottom edge和line box的text box bottom edge来对齐排列
Aligning the Element's outer edges relative to the line box's outer edges
1. top: element的top edge aligned to the line box's top edge;
2. bottom: element的bottom edge aligned to the line box's bottom edge.
Vertical-align之所以有那样的align行为的原因:
我们现在可以更清晰地看看在不同场景下的vertical alignment.特别地,我们会专门挑一些可能会出错的场景来看。
centering an icon
常常困扰过我的一个问题是下面的需求:我有一个icon,我希望将这个icon和紧挨着它的文本实现居中对齐。如果仅仅给这个icon一个vertical-align: middle的设置貌似并不能令人满意地实现居中对齐。
看下面的例子:
<!-- 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>
我们将图例增加一些参考线,形成上面更加细致的图。有了这张图,我们就可以看到问题出在哪里了。由于左面的'Centered?'文字未做任何align设置,因此默认这个文本就sits on the baseline。这里隐含这样一个事实:我们对于未做align设置的"Centered?"文本和前面的icon vertical-align:middle来做对齐时,实际上我们是和小写字母的中间部分来做对齐的,而那些有伸展的比如大写的C字母就会往上突出一点,也就是说并没有完全意义的center对齐。
而在右边的例子中,我们将整个font作为一个整体来做居中对齐。文本的baseline轻微地比linebox的baseline向下移动一点空间来达到这个中间对齐的目的。这样icon和紧挨着它的文本就实现了完全的居中对齐
移动linebox的baseline
移动linebox的baseline是我们使用vertical-align属性时常见的陷阱: linebox的baseline位置会被这行的所有元素共同影响。我们来假设,一个元素是这样来排列的: linebox的baseline必须移动。既然大部分vertical alignment(除了top和bottom外)是相对于这个line box的baseline的,那么如果这个baseline移动,那么本行的其他元素也将随着移动。
一些例子:
1.看下面的图例:
如果在一行中有一个高的元素跨越了整个line-height, vertical-align对这个元素就不会有任何效果。因为在它的上面和他的下面都没有任何空间让它来做移动。为了满足这个元素也是相对于linebox的baseline来对齐的基本要求,那么line box的baseline必须做相应地移动(才能满足那个条件)。另外一个矮的元素有vertical-align:baseline的设置。在左面的那个例子,高的box设置为vertical-align:text-bottom,而在右面的例子中这个高的box则设置为vertical-align: text-top.你可以看到line-box的baseline裹带着矮的那个box向上面跳动了
<!-- 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>
如果我们将tall element使用其他的vertical-align属性值,也会发生类似的事情。
2.即使我们设置高元素的vertical-align为bottom(左面的例子)或者top(右面的例子)也会将linebox的baseline移动起来。这有些奇怪,因为baseline并没有被involve啊。
<!-- 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>
3. 在一行中,我们放置两个大的元素并且垂直对齐他们将会移动baseline来满足他们的对齐要求。那么linebox的高度将会被调整(左图)再增加第三个元素,这个元素本身不会超过linebox的边缘edges将既不会影响linebox的高度,也不会影响baseline的位置。如果这个第三个元素本身超过了linebox的边缘,则linebox的高度和baseline将再次调整以适应这个变化。这个第三种场景前两个box将被向下push
<!-- 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>
在inline elements的底部可能会产生小小的gap。看下面的设置,这在你视图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>
正如你看到的,list items sit on the baseline, 在baseline的下方是预留给下行字母(比如p,g,j等)的空间,这就导致了下方gap的产生。解决方案是??只要将baseline移出就可以了,比如通过设置vetical-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>
这种场景对于已经有了text content的inline-blocks并不会产生因为content就会将baseline向上面移动
注意:inline-level元素之间的这个gap往往会破坏layout.这是inline-level的元素本身的特性或者说是问题。但是由于这种gap往往是vertical-align本身的需要,所以需要了解这个事实。
在前面的li例子中,你可以看到这个gap实际上产生于markup中元素之间的white-space。所遇元素之间的white-space都会collapsed into one space.这个空白符由于会占用空间,因此,如果我们将两个紧挨的inline元素各自给 50%的话,由于没有足够的饿空间(因为多了一个空白字符的宽度),所以一行将会变成两行,而这将会破坏我们的layout。要解决这个问题,我们就需要清除两个元素间的空白符,比如我们可以使用html comment来解决这类问题。
<!-- 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就是这些知识点。一旦你清楚了对应的rule,实际上并不复杂。如果vertical-align不如你的预期工作,你可以问以下问题:
1. line box的baseline和top,bottom edge在哪里?
2. inline-level元素的baseline,top,bottom edge在哪里?
回答了这两个问题,你就能解决所有vertical-align的相关问题