css vertical-align你真的很了解嘛?

css vertical-align你真的很了解嘛?

與vertical-align對應的是text-align,text-align設置元素里內容在水平方向上的對齊方式。而vertical-align屬性,通常我們的理解是設置該元素里內容在豎直方向上的對齊方式,這種理解是錯誤的。為什麼錯誤?請大家往下看。

接下來,我們先看幾個demo,vertical-align在其中扮演了什麼作用。(以下有些名詞大家若是不懂,請直接跳過這一段)。

具體查看代碼。

第一組: 父元素div內兩個子div,子div內容為空,只是單純的給定寬高,不改變vertical-align屬性。

第二組: 父元素內兩個子div,第二個子div內容有一個<span>right-span</span>,不改變vertical-align屬性。

第三組: 父元素內兩個子div,第二個子div內容有一個<span>right-span</span>,vertical-align屬性設置為top。

第四組: 父元素內兩個子div,兩個子div里各有span標籤,不改變vertical-align屬性。

結果圖如下:

現在簡單的解釋一下,為什麼是這樣的原因?(看不懂的請跳過這一段,讀完這篇文章,估計就能理解了)以下行盒子指的都是兩個子div的父元素。

第一組:行盒子(子div的父元素)里,是兩個沒有內容的div(display:inline-block),這兩個div(inline box)的基線(baseline)值都是盒子margin-bottom的邊界。兩者對齊方式以baseline對齊,所以兩個div水平對齊。

第二組:行盒子里,第一個div沒有內容,基線是盒子的margin-bottom邊界。第二個div內里有line box,所以基線就是內容文字"right-span"的基線。默認對齊方式也是以baseline對齊(vertical-align默認值baseline),所以會呈現上述樣子。

第三組: 設置第二個div的vertical-align為top,兩個div就會水平對齊。設置為top,就不再是按照baseline對齊了,設置vertical-align:top,會讓該元素的頂部(盒子的頂部邊界,如果有設置margin-top值,那麼頂部邊界也就包括margin-top)與行盒子(line box)的頂部對齊。

也可以設置兩個div的vertical-align的值都為middle,那麼原理是?設置vertical-align:middle,會讓該元素盒子的垂直中點與父元素(行盒子)的baseline值+x-height的一半(字母x高度的一半)位置對齊。現在只設置第一個div的vertical-align為middle,那麼效果如下圖左側顯示,該元素盒子的垂直中點與行盒子的baseline+x-height一半對齊(行盒子的baseline的定位請看下文解釋)。

如果此時繼續設置第二個div的vertical-align為middle,那麼效果如下圖右側顯示,兩個div就水平對齊了,因為第二個div的垂直中點要繼續和行盒子的baseline+x-height一半對齊(該線,接下來我將稱其為對齊線),對齊線在設置第一個div vertical-align為middle時已經確定下來了,也就是下圖長長的紅線。因而兩個div水平就對齊了。

第四組:兩個div都有內容元素,vertical-align默認值是baseline,每個div的基線就是盒子里最後一個內容元素的baseline。兩個div的基線對齊,因而兩個div水平對齊。

接下來就給大家詳細講解下vertical-align是什麼,幹什麼?

一:w3c中對vertical-align描述的相關信息。

vertical-alignValue: baseline | sub | super | top | text-top | middle | bottom | text-bottom | <percentage> | <length> | inheritInitial: baselineApplies to: inline-level and table-cell elementsInherited: noPercentages: refer to the line-height of the element itselfMedia: visualComputed value: for <percentage> and <length> the absolute length, otherwise as specifiedThis property affects the vertical positioning inside a line box of the boxes generated by an inline-level element.

最後一句介紹vertical-align這個屬性影響由inline-level(內聯級)元素生成的盒子,在行盒子中豎直方向的對齊位置。

講解一下兩個名詞:

1. inline-level element (內聯級元素)。內聯級元素包括 display屬性計算值為

  • inline 內聯元素一般是用來包裹文本的元素,比如span、strong、em標籤等
  • inline-block 內聯-塊元素(內嵌的塊元素)可以在一行中排列顯示,以具有width,height(也有可能是通過其內容確定的)和padding,border及margin。比如img、input標籤等
  • line-table(本文不考慮)

2. line box(行盒子):內聯級元素(inline-level elements)在一行中一個挨一個地排列,一旦當前行放不下了,就在它下方創建一個新行,所有這些行都是所謂的行盒(line box),用來包住這一行的所有內容。不同大小的的內容意味著不等高的行盒。下圖中行盒的上下邊界用紅線標出來了:

在每一個line box中,我們都可以使用vertical-align來對齊line box之中的元素。

二:什麼是baseline。

字母x與baseline的關係:字母x的下邊緣(線)就是基線。

下圖是英文字母在四線格中,每條線的含義(常用的英文本就是四線格的)。

:如何確定inline-level elements(內聯級元素)盒子的baseline位置。

display屬性計算值為

  • inline,內聯元素的baseline,是裡面文本(即使沒有字母x,可以想像文本中有一個字母x)字母x的下邊緣線
  • inline-block,內聯塊元素baseline位置的確定規則又分為以下三種(baseline位置參照下圖):

(1) inline-block元素盒子里,沒有內容(流內內容),是一個空的盒子時,baseline位置就是該盒子margin-bottom的邊界(沒有margin-bottom值,就是盒子的邊界值)。如下圖左側div

(2)inline-block元素盒子里,有內容元素,並且overflow屬性值為visible時(默認值),那麼該盒子的baseline位置就是裡面最後一個內容元素的baseline。如下圖中間div

(3)inline-block元素盒子里,有內容元素,並且overflow屬性值為非visible時 (比如overflow:hidden),那麼該盒子的baseline位置就是該盒子margin-bottom的邊界。如下圖右側div

四:如何確定父元素(行盒子)的baseline位置。

有人說是父元素里最後一個沒有設置任何對齊方式的inline-level box(內聯級盒子元素)的baseline。(來源)

由於行盒子的baseline是不可見的,無法直觀的確定,如果所有子元素都設置對齊方式了,可以通過在父元素內容最後添加一個字元『x』來確定父元素的baseline。如果想直觀的看下行盒子baseline的位置,可以通過在行盒子css類上添加一個after偽元素選擇器,內容為『x字元,那麼行盒子的baseline就是x字母的下邊緣線。但是行盒子的baseline位置會受裡面子元素影響而移動(相應字母x的位置也就變動了)

.line-box::after { content: x;}

四:元素vertical-align取不同的值,與行盒(父元素)baseline的對齊關係

這裡先總結下,設置元素的vertical-align屬性,並沒有改變該元素的baseline位置(元素baseline位置確定參照 :如何確定inline-level elements(內聯級元素)盒子的baseline位置。),改變的只是與父元素基線對齊的位置,所以父元素的基線位置是會移動的

以下示例,html結構和css都是以此基礎:

css: .line-box { position: relative; padding-left: 100px; border: 1px solid black; } .child-box1, .child-box2 { display: inline-block; width: 100px; height: 100px; border: 1px solid red; } .line { position: absolute; top: 100px; height: 1px; width: 300px; background: black; }html: <div class="line-box"> <!-- 橫線,測試用,用來比對兩個子元素和父元素對齊的位置 --> <div class="line"></div> <div class="child-box1"></div> <div class="child-box2"></div> </div>

默認情況,不設置任何值

具體代碼和效果

效果圖:

兩個子元素(紅色邊框的盒子)內容都為空。因此兩個子元素(紅色邊框的盒子)的基線(baseline)都為盒子的底部邊界(若有margin-bootom,則為margin-bottom)

大家可能會有疑問,為什麼紅色邊框盒子與行盒子之間會有條縫隙?大家先別著急,往下看大概就會明白了。

先給行盒子內容最後添加一個字母『x來直觀的確定下行盒子的baseline位置。

具體代碼和效果

代碼

.line-box::after { content: x;}

效果圖

用來測試的橫線的位置就是上圖那條虛線的位置,兩個子元素以各自的基線和父元素的基線對齊。

可以看到兩個子元素的baseline和行盒子的baseline對齊,其實字母x』所佔據的空間,在baseline的下方還會有一部分。字母x的整個空間大小如圖上藍色區域所示。所以就會有那麼一條空白縫隙存在~.~。

那麼如何去掉這條縫隙呢?大家想一下,現在是子元素的baseline基於行盒子的baseline對齊,導致行盒子里的字母『x』臨近行盒子底部,字母『x』baseline以下部分所佔據的空間撐起這條縫隙。所以我們可以讓子元素以別的位置(vertical-align:除了baseline以外其他任何值)對齊行盒子的baseline,這樣字母x就不會臨近行盒子底部,也就不會有所謂的縫隙了。

即使父元素里沒有字母x,只有兩個子元素盒子,還會存在該縫隙。所以我們可以假想一下,父元素裡面,內容最後總是存在一個沒有沒有任何對齊方式的內聯元素。

1.vertical-align: middle

讓子元素盒子垂直中點與行盒子的baseline+字母『x高度的一半對齊

(w3c: Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.)

具體代碼和效果

代碼:

css.child-box1, .child-box2 { vertical-align: middle;}.line { top: 50px;}

效果圖

兩個子元素以垂直中點和父元素的基線+字母x高度的一半對齊

2. vertical-align: text-top

子元素盒子的頂部和行盒子里內容區域的頂部對齊

(w3c: Align the top of the box with the top of the parents content area)

具體代碼和效果

css .child-box1, .child-box2 { vertical-align: text-top; } .line { top: 0px; }

效果圖

兩個子元素以各自盒子的頂部和行盒子內容區域的頂部對齊

3. vertical-align: text-bottom

子元素盒子的底部和行盒子里內容區域的底部對齊

(w3c:Align the bottom of the box with the bottom of the parents content area)

具體代碼和效果

css.child-box1, .child-box2 { vertical-align: text-bottom}.line { top: 101px; // 子元素盒子的底部包包括1像素的底部邊框}

效果圖:

兩個子元素以各自盒子的底部和行盒子內容區域的底部對齊

4. vertical-align: sub

降低子元素盒子的基線到父元素盒子下標的位置

(w3c:Lower the baseline of the box to the proper position for subscripts of the parents box. (This value has no effect on the font size of the elements text.) )

這裡首先需要理解一下,下標的概念。我們可以向父元素盒子里直接添加一個sub標籤<sub> sub test </sub>,看下效果就明白了。

子元素盒子未設置vertical-align: sub;

具體代碼和效果

代碼:

css: .line { top: 50px; }html: <div class="line-box"> <div class="line"></div> <div class="child-box1"> </div> <div class="child-box2"> </div> <sub>sub test</sub> </div>

效果:

設置第一個子元素盒子的vertical-align: sub

具體代碼和效果

.child-box1 { vertical-align: sub;}

效果圖:

可以看見,設置vertical-align:sub的第一個元素盒子的基線和父元素盒子下標內容的基線對齊。第二個元素沒有設置vertical-align屬性(默認值baseline)基線和行盒子的基線對齊

5. vertical-align: super

升高子元素盒子的基線到父元素盒子上標的位置

( w3c :Raise the baseline of the box to the proper position for superscripts of the parents box. (This value has no effect on the font size of the elements text.) )

這裡我們理解下上標的概念。我們可以向父元素盒子里直接添加一個上標sup標籤<sup> sup test </sup>,看下效果就明白了。

第一個子元素盒子設置vertical-align: super

具體代碼和效果

代碼:

css: .child-box1 { vertical-align: super; } .line { top: 101px; }html: <div class="line-box"> <div class="line"></div> <div class="child-box1"> </div> <div class="child-box2"> </div> <sup>sup test</sup> </div>

效果圖:

第一個子元素(設置vertical-align:super)盒子的基線和行盒子上標內容基線位置對齊。第二個子元素未設置任何值,基線和行盒子基線對齊

6. vertical-align: <percentage>

百分比的值是相對該元素的line-height數值的(元素有默認行高的),具體的升高/降低數值由由該元素的line-height的值乘以百分比計算得出。相對自己baseline,升高或較低該元素一定距離。

0%位置就是默認的baseline

(w3c : Raise (positive value) or lower (negative value) the box by this distance (a percentage of the line-height value). The value 0% means the same as baseline. )

代碼:

.child-box1 { line-height: 100px; vertical-align: 10%;}.line { // 子元素盒子高度100px + 2px上下兩邊框 + 相對自己baseline位置向上移動10px top: 112px;}

為了明顯看出效果,設置第一個元素盒子的line-height:100px;(不使用默認值),設置第一個元素盒子的vertical-align: 10%; 因此該元素相對baseline移動(向上)的距離為100px * 10% = 10px。

7. vertical-align: length

該值為一定的像素數值,與vertical-align:percentage效果類似,除了移動的距離是被計算出來的。

接下來兩個值是子元素盒子的邊界相對於行盒子的邊界進行對齊

8. vertical-align: top

子元素盒子的頂部相對行盒子的頂部對齊

(w3c: Align the top of the aligned subtree with the top of the line box.)

代碼:

具體代碼和效果

第一個元素盒子未設置vertical-align:top

css .child-box1 { margin-top: 20px; }html <div class="line-box"> <div class="child-box1"> </div> <div class="child-box2"> </div> </div>

設置第一個子元素盒子的margin-top為20px,效果圖如下

雖然一個子元素盒子,設置margin-top值,但是該子元素盒子的基線沒有改變(任何時候都不會改變),最終基線對齊行盒子基線。

而第二個子元素盒子雖然沒設置margin-top值,但是最終也是以底部基線對齊行盒子基線,所以效果圖如上。

第一個子元素盒子設置vertical-align:top,也設置margin-top:20px

效果圖如下:

第一個子元素設置vertical-align: top,以盒子頂部和行盒子頂部對齊,所以該元素也就不是基於基線對齊了。那麼第二個子元素和字母x位置就變動了(整體上移)

這時候大家想一下,如果字母x也以vertical-align:top對齊行盒子頂部,效果會怎麼樣?第二個子元素盒子如何對齊?

具體代碼和效果

代碼:

css .line-box::after { content: x; vertical-align: top; } .child-box1 { margin-top: 20px; vertical-align: top; }html <div class="line-box"> <div class="child-box1"> </div> <div class="child-box2"> </div> <span>xxx</span> </div>

效果圖:

行盒子基線,並沒有因為最後一個字母x設置vertical-align:top頂部對齊而改變。

其實行盒子的基線,是最後一個沒有設置任何對齊方式的內聯級元素的基線

(看上述四:如何確定父元素(行盒子)的baseline位置。

8. vertical-align: bottom

子元素盒子的底部和行盒子的底部對齊

(w3c:Align the bottom of the aligned subtree with the bottom of the line box.)

效果圖

五:使用vertical-align常見的案例

1. 左側是一個文字,右側是一個圖標,進行水平對齊.

初始情況,未設置任何值

代碼:

html <div class="line-box"> <span class="label">顏色xx</span> <span class="icon"></span> </div>css .line-box { padding-left: 100px; border: 1px solid black; } .icon { display: inline-block; width: 20px; height: 20px; background: blue; } .label { }

效果圖:

可以看見,是文字的baseline(字母x的下邊緣)和藍色盒子的底部邊界值(baseline)對齊

接下來,讓我們設置文字和圖標的vertical-align:middle

css.icon, .label { vertical-align: middle;}

效果

現在文字和圖標已經水平對齊了。

大家發現了沒,文字(span標籤)的上方(注意是上方)與行盒子(父元素)之間有條縫隙。接下來說下為什麼會存在這條縫隙。現在理論上(實際上)文字和圖標的垂直中點已經和行盒子的基線+字母x高度的一半對齊。我們給行盒子加個偽元素看下行盒子基線的位置

.line-box::after { content: x}

其實行盒子最後,會存在一個我們看不見的文本盒(w3c稱作struct),這裡我們通過添加一個偽元素,添加一個字母『x』,直觀的模擬下,該文本盒也會有默認大小(除非font-size設為0)的。所以該縫隙,就是由於文本盒本身空間所佔據的。

這裡我們試一下將圖標高度變大

.icon { height: 100px}

可以看見,圖標高度變大,圖標撐高行盒子,縫隙就不存在了。

我們也可以設置行盒子的font-size:0,文字和圖標的字體大小單獨設置,也會把縫隙消除。

.line-box { font-size: 0;}.label { font-size: 14px;}

如果文字的font-size設置的比較大,也會把行盒子撐開

.label { font-size: 20px;}

參考文獻

  1. 深入理解css中vertical-align屬性
  2. w3c vertical-align
  3. vertical-align刨根問底
  4. 關於vertical-align屬性
  5. 關於VERTICAL-ALIGN,你需要知道的一些事兒
  6. Vertical-Align: All You Need To Know

推薦閱讀:

十年web前端開發工程師告訴你怎樣零基礎入門
關於瀏覽器兼容問題的一些名詞和工具
生成前端代碼的一些工具介紹
#Head First HTML/CSS 讀書筆記(CSS篇)
2016主流前端框架部分整理

TAG:前端入門 | CSS | 萬維網聯盟W3C |