Vertical-Align: 關於inline,inline-block文字排版

世有因果知因求果發表於2016-10-20

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元素各自給width: 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的相關問題

 

相關文章