深刻理解 CSS3 彈性盒佈局模型

彈性盒佈局模型(Flexible Box Layout)是 CSS3 規範中提出的一種新的佈局方式。該佈局模型的目的是提供一種更加高效的方式來對容器中的條目進行佈局、對齊和分配空間。這種佈局方式在條目尺寸未知或動態時也能工做。這種佈局方式已經被主流瀏覽器所支持,能夠在 Web 應用開發中使用。本文詳細的介紹該佈局模型以及如何在具體開發中應用該佈局模型來簡化常見的頁面佈局場景。css

深刻理解 CSS3 彈性盒佈局模型

Web 應用的樣式設計中,佈局是很是重要的一部分。佈局用來肯定頁面上不一樣組件和元素的尺寸和位置。隨着響應式用戶界面的流行,Web 應用通常都要求適配不一樣的設備尺寸和瀏覽器分辨率。響應式用戶界面設計中最重要的一環就是佈局。須要根據窗口尺寸來調整佈局,從而改變組件的尺寸和位置,以達到最佳的顯示效果。這也使得佈局的邏輯變得更加複雜。本文介紹的是 CSS3 規範中引入的新佈局模型:彈性盒模型(flex box)。彈性盒模型能夠用簡單的方式知足不少常見的複雜的佈局需求。它的優點在於開發人員只是聲明佈局應該具備的行爲,而不須要給出具體的實現方式。瀏覽器會負責完成實際的佈局。該佈局模型在主流瀏覽器中都獲得了支持。css3

簡介

引入彈性盒佈局模型的目的是提供一種更加有效的方式來對一個容器中的條目進行排列、對齊和分配空白空間。即使容器中條目的尺寸未知或是動態變化的,彈性盒佈局模型也能正常的工做。在該佈局模型中,容器會根據佈局的須要,調整其中包含的條目的尺寸和順序來最好地填充全部可用的空間。當容器的尺寸因爲屏幕大小或窗口尺寸發生變化時,其中包含的條目也會被動態地調整。好比當容器尺寸變大時,其中包含的條目會被拉伸以佔滿多餘的空白空間;當容器尺寸變小時,條目會被縮小以防止超出容器的範圍。彈性盒佈局是與方向無關的。在傳統的佈局方式中,block 佈局是把塊在垂直方向從上到下依次排列的;而 inline 佈局則是在水平方向來排列。彈性盒佈局並無這樣內在的方向限制,能夠由開發人員自由操做。web

在深刻到彈性盒佈局模型的細節以前,首先了解幾個相關的重要概念,具體如圖 1所示。瀏覽器

圖 1. 彈性盒佈局模型相關的概念

彈性盒佈局模型相關的概念

彈性盒佈局的容器(flex container)指的是採用了彈性盒佈局的 DOM 元素,而彈性盒佈局的條目(flex item)指的是容器中包含的子 DOM 元素。圖中的最外圍的邊框表示的是容器,而編號 1 和 2 的邊框表示的是容器中的條目。工具

從圖中能夠看到,彈性盒佈局中有兩個互相垂直的座標軸:一個稱之爲主軸(main axis),另一個稱之爲交叉軸(cross axis)。主軸並不固定爲水平方向的 X 軸,交叉軸也不固定爲垂直方向的 Y 軸。在使用時,經過 CSS 屬性聲明首先定義主軸的方向(水平或垂直),則交叉軸的方向也相應肯定下來。容器中的條目能夠排列成單行或多行。主軸肯定了容器中每一行上條目的排列方向,而交叉軸則肯定行自己的排列方向。能夠根據不一樣的頁面設計要求來肯定合適的主軸方向。有些容器中的條目要求從左到右水平排列,則主軸應該是水平方向的;而另一些容器中的條目要求從上到下垂直排列,則主軸應該是垂直方向的。佈局

肯定主軸和交叉軸的方向以後,還須要肯定它們各自的排列方向。對於水平方向上的軸,能夠從左到右或從右到左來排列;對於垂直方向上的軸,則能夠從上到下或從下到上來排列。對於主軸來講,排列條目的起始和結束位置分別稱爲主軸起始(main start)和主軸結束(main end);對於交叉軸來講,排列行的起始和結束位置分別稱爲交叉軸起始(cross start)和交叉軸結束(cross end)。在容器進行佈局時,在每一行中會把其中的條目從主軸起始位置開始,依次排列到主軸結束位置;而當容器中存在多行時,會把每一行從交叉軸起始位置開始,依次排列到交叉軸結束位置。post

彈性盒佈局中的條目有兩個尺寸:主軸尺寸和交叉軸尺寸,分別對應其 DOM 元素在主軸和交叉軸上的大小。若是主軸是水平方向,則主軸尺寸和交叉軸尺寸分別對應於 DOM 元素的寬度和高度;若是主軸是垂直方向,則兩個尺寸要反過來。與主軸和交叉軸尺寸對應的是主軸尺寸屬性和交叉軸尺寸屬性,指的是 CSS 中的屬性 width 或 height。好比,當主軸是水平方向時,主軸尺寸屬性是 width,而 width 的值是主軸尺寸的大小。flex

彈性盒佈局模型中的 CSS 樣式聲明分別適用於容器或條目。在下面的內容中會詳細的介紹相關的 CSS 屬性。首先介紹如何使用彈性盒佈局模型進行基本的頁面佈局。在本文的全部代碼示例中,容器的 CSS 類名統一爲 flex-container,而條目的 CSS 類名則爲 flex-item。全部的示例均可以在CodePen上進行預覽。flexbox

 

基本佈局

首先從最基本的佈局開始介紹彈性盒佈局模型。要實現的佈局效果是一個簡單的圖片縮略圖預覽頁面。頁面的基本 HTML 如代碼清單 1所示。spa

清單 1. 簡單的圖片縮略圖預覽頁面的 HTML 代碼
<ul class="flex-container">
   <li class="flex-item"><img src="http://placehold.it/300&text=1"></li>
   <li class="flex-item"><img src="http://placehold.it/300&text=2"></li>
   <li class="flex-item"><img src="http://placehold.it/300&text=3"></li>
   <li class="flex-item"><img src="http://placehold.it/300&text=4"></li>
   <li class="flex-item"><img src="http://placehold.it/300&text=5"></li>
   <li class="flex-item"><img src="http://placehold.it/300&text=6"></li>
</ul>

該頁面的基本 HTML 是很簡單的。在一個<ul>元素下面有 6 個<li>元素。每一個<li>元素中包含一個大小爲 300x300 的縮略圖圖片。<ul>元素做爲彈性盒佈局的容器,而<li>元素則是容器中的條目。實現基本佈局的 CSS 如代碼清單 2所示。

清單 2. 簡單的圖片縮略圖預覽頁面的 CSS 代碼
.flex-container {
  list-style: none;

   display: flex;
   flex-direction: row;
   flex-wrap: wrap;
}

.flex-item {
   padding: 5px;
}


在代碼清單 2 中,對於彈性盒佈局的容器,使用"display: flex"聲明使用彈性盒佈局。CSS 屬性聲明"flex-direction"用來肯定主軸的方向,從而肯定基本的條目排列方式。"flex-direction"屬性的可選值及其含義如1所示。

表 1. 「flex-direction」屬性的可選值及其含義
屬性值 含義
row(默認值) 主軸爲水平方向。排列順序與頁面的文檔順序相同。若是文檔順序是 ltr,則排列順序是從左到右;若是文檔順序是 rtl,則排列順序是從右到左。
row-reverse 主軸爲水平方向。排列順序與頁面的文檔順序相反。
column 主軸爲垂直方向。排列順序爲從上到下。
column-reverse 主軸爲垂直方向。排列順序爲從下到上。

默認狀況下,彈性盒容器中的條目會盡可能佔滿容器在主軸方向上的一行。當容器的主軸尺寸小於其全部條目總的主軸尺寸時,會出現條目之間互相重疊或超出容器範圍的現象。CSS 屬性"flex-wrap"用來聲明當容器中條目的尺寸超過主軸尺寸時應採起的行爲。"flex-wrap"屬性的可選值及其含義如2所示。

表 2. 「flex-wrap」屬性的可選值及其含義
屬性值 含義
nowrap(默認值) 容器中的條目只佔滿容器在主軸方向上的一行,可能出現條目互相重疊或超出容器範圍的現象。
wrap 當容器中的條目超出容器在主軸方向上的一行時,會把條目排列到下一行。下一行的位置與交叉軸的方向一致。
wrap-reverse 與 wrap 的含義相似,不一樣的是下一行的位置與交叉軸的方向相反。

可使用"flex-flow"屬性把"flex-direction"和"flex-wrap"結合起來,如代碼清單 3所示。

清單 3. 「flex-flow」屬性的使用示例
flex-flow: row wrap;
 

容器中的條目

除了彈性盒佈局模型中的容器以外,容器中的條目也能夠經過 CSS 屬性來改變其佈局行爲。下面介紹容器中的條目可使用的 CSS 屬性聲明。

條目的順序

默認狀況下,容器中條目的順序取決於它們在 HTML 標記代碼中的出現順序。能夠經過"order"屬性來改變條目在容器中的出現順序。對於代碼中的 HTML 標記添加代碼清單 4中的 CSS 聲明,運行以後的效果是最後一個<li>元素出如今了其餘<li>元素的最前面。預覽的頁面見這裏

清單 4. 「order」屬性的使用示例
.flex-item:last-child {
   order: -1;
}

"order"屬性的主要做用是兼顧頁面的樣式和可訪問性。支持可訪問性的設備,如屏幕閱讀器,都是按照 HTML 中代碼的順序來讀取元素的。這就要求一些相對重要的文本出如今 HTML 標記的前面。而對於使用瀏覽器的通常用戶來講,在某些狀況下把一些相對不重要的圖片顯示在前面是更好的選擇。好比在一個商品的展現頁面中,在源代碼中把商品描述的文本放在商品圖片以前。這樣能夠方便屏幕閱讀器的讀取;而在 CSS 中使用"order"屬性把圖片放在文本的前面。這樣可讓用戶首先看到圖片。

條目尺寸的彈性

彈性盒佈局模型的核心在於容器中條目的尺寸是彈性的。容器能夠根據自己尺寸的大小來動態地調整條目的尺寸。當容器中有空白空間時,條目能夠擴展尺寸以佔據額外的空白空間;當容器中的空間不足時,條目能夠縮小尺寸以防止超出容器範圍。條目尺寸的彈性由 3 個 CSS 屬性來肯定,分別是"flex-basis"、"flex-grow"和"flex-shrink"。

"flex-basis"屬性聲明接受的值與"width"屬性是同樣的,用來肯定彈性條目的初始主軸尺寸。這是條目的尺寸被容器調整以前的初始值。若是"flex-basis"的值爲 auto,則實際使用的值是主軸尺寸屬性的值,即 width 或 height 屬性的值。若是主軸尺寸屬性的值也是 auto,則使用的值由條目內容的尺寸來肯定。

"flex-grow"屬性的值是一個沒有單位的非負數,默認值是 1。"flex-grow"屬性的值表示的是當容器有多餘的空間時,這些空間在不一樣條目之間的分配比例。好比,一個容器中有 3 個條目,其"flex-grow"屬性的值分別爲 1,2 和 3,那麼當容器中有空白空間時,這 3 個條目所得到的額外空白空間分別佔所有空間的 1/六、1/3 和 1/2,如代碼清單 5所示。預覽的頁面見這裏

清單 5. 「flex-grow」屬性的使用示例
.flex-item:nth-child(1) {
   flex-grow: 1;
}

.flex-item:nth-child(2) {
   flex-grow: 2;
}

.flex-item:nth-child(3) {
   flex-grow: 3;
}

"flex-shrink"屬性在使用上相似於"flex-grow"。該屬性的值也是無單位的,表示的是當容器的空間不足時,各個條目的尺寸縮小的比例。在進行尺寸縮小時,條目的縮小比例與"flex-basis"的值相乘以後,就獲得了應該縮小的尺寸的實際值。例如,在容器中有 3 個條目,其"flex-shrink"屬性的值分別爲 1,2 和 3。每一個條目的主軸尺寸均爲 200px。當容器的主軸尺寸不足 600px 時,好比變成了 540px 以後, 則須要縮小的尺寸 60px 由 3 個條目按照比例來分配。3 個條目分別要縮小 10px、20px 和 30px,主軸尺寸分別變爲 190px、180px 和 170px。預覽的頁面見這裏

使用屬性"flex"能夠同時聲明"flex-basis"、"flex-grow"和"flex-shrink"的值,格式是"none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]"。該屬性的值的 3 個組成部分的初始值分別是"0 1 auto"。當屬性"flex"的值爲 none 時,至關於"0 0 auto"。當組成部分"flex-basis"被省略時,其值爲 0%。代碼清單 6給出了屬性"flex"的使用示例。

清單 6. 屬性「flex」的使用示例
.flex-item:nth-child(1) {
   flex: 1 1 auto;
}

flex: 1;  // flex-grow 的值爲 1,flex-shrink 的值爲 1,flex-basis 的值爲 0%。

須要注意的是,在容器分配額外空間時是以"行"爲單位的。容器先根據"flex-wrap"的屬性值來肯定是單行佈局或多行佈局,而後把條目分配到對應的行中,最後在每一行內進行空白空間的分配。如代碼清單 7中的 CSS 聲明示例,在容器中有 4 個條目。

清單 7. 多行佈局的 CSS 聲明
.flex-container {
 width: 990px;
}

.flex-item {
 width: 300px;
 flex: auto;
}

因爲容器的寬度只有 990px,因此在一行中只能排列 3 個條目,而剩下的 1 個條目則會被排列到單獨的一行中。在 3 個條目的行中,多餘的空間 90px 被平均分配給 3 個條目;而在一個條目單獨的行中,多餘的 690px 被該條目徹底佔據。

條目對齊

當容器中的條目的尺寸肯定以後,能夠設置這些條目在容器中的對齊方式。對齊條目的方式有 3 種。

第一種方式是使用自動空白邊,即"margin: auto"。在使用自動空白邊時,容器中額外的空白空間會被聲明爲 auto 的空白邊佔據,如代碼清單 8所示。CSS 聲明 profile 中經過"margin-left: auto"使得該條目左邊的空白邊會佔據額外的空間,從而"Profile"文本會靠右顯示。預覽的頁面見這裏

清單 8. 使用自動空白邊進行條目對齊
<div class="flex-container">
  <div class="logo">Logo</div>
  <div class="profile">Profile</div>
</div>
.flex-container {
  display: flex;
}
.profile {
  margin-left: auto;
}

第二種方式是在主軸方向上的對齊。這是經過容器上的"justify-content"屬性來進行設置,能夠調整條目在主軸方向上的對齊方式。這種條目對齊方式的調整發生在修改條目的彈性尺寸和處理自動空白邊以後。當容器中的一行中的條目沒有彈性尺寸,或是已經達到了它們的最大尺寸時,在這一行上可能還有額外的空白空間。使用"justify-content"屬性能夠分配這些空間。該屬性還能夠控制當條目超出行的範圍時的對齊方式。"justify-content"屬性的可選值和含義如3所示,實際的佈局效果見圖 2

表 3. 「justify-content」屬性的可選值和含義
屬性值 含義
flex-start 條目集中於該行的起始位置。第一個條目與其所在行在主軸起始方向上的邊界保持對齊,其他的條目按照順序依次排列。
flex-end 條目集中於該行的結束方向。最後一個條目與其所在行在主軸結束方向上的邊界保持對齊,其他的條目按照順序依次排列。
center 條目集中於該行的中央。條目都往該行的中央排列,在主軸起始方向和結束方向上留有一樣大小的空白空間。若是空白空間不足,則條目會在兩個方向上超出一樣的空間。
space-between 第一個條目與其所在行在主軸起始方向上的邊界保持對齊,最後一個條目與其所在行在主軸結束方向上的邊界保持對齊。空白空間在條目之間平均分配,使得相鄰條目之間的空白尺寸相同。
space-around 相似於 space-between,不一樣的是第一個條目和最後一個條目與該行的邊界之間一樣存在空白空間,該空白空間的尺寸是條目之間的空白空間的尺寸的一半。
圖 2. 「justify-content」屬性不一樣值的佈局效果

「justify-content」屬性不一樣值的佈局效果

第三種方式是交叉軸方向上的對齊。除了在主軸方向上對齊以外,條目也能夠在交叉軸方向上對齊。容器上的屬性"align-items"用來設置容器中全部條目在交叉軸上的默認對齊方向,而條目上的屬性"align-self"用來覆寫容器指定的對齊方式。屬性"align-items"的可選值和含義如4所示,實際的佈局效果見圖 3

表 4. 屬性「align-items」的可選值和含義
屬性值 含義
flex-start 條目與其所在行在交叉軸起始方向上的邊界保持對齊。
flex-end 條目與其所在行在交叉軸結束方向上的邊界保持對齊。
center 條目的空白邊盒子(margin box)在交叉軸上居中。若是交叉軸尺寸小於條目的尺寸,則條目會在兩個方向上超出相同大小的空間。
baseline 條目在基準線上保持對齊。在全部條目中,基準線與交叉軸起始方向上的邊界距離最大的條目,它與所在行在交叉軸方向上的邊界保持對齊。
stretch 若是條目的交叉軸尺寸的計算值是"auto",則其實際使用的值會使得條目在交叉軸方向上儘量地佔滿。

屬性"align-self"的可選值除了表中列出的以外,還能夠設置爲"auto"。當"align-self"的值爲 auto 時,其計算值是父節點的屬性"align-items"的值。若是該節點沒有父節點,則計算值爲"stretch"。

圖 3. 屬性「align-items」不一樣值的佈局效果

屬性「align-items」不一樣值的佈局效果

交叉軸空白處理

當容器在交叉軸方向上有空白空間時,屬性"align-content"用來對齊容器中的行。該屬性的做用相似於"justify-content",只不過"justify-content"是在主軸方向上對齊行中的條目。當容器中只有單行時,該屬性不起做用。屬性"align-content"的可選值和含義如5所示,實際的佈局效果見圖 4

表 5. 屬性「align-content」的可選值和含義
屬性值 含義
flex-start 行集中於容器的交叉軸起始位置。第一行與容器在交叉軸起始方向上的邊界保持對齊,其他行按照順序依次排列。
flex-end 行集中於容器的交叉軸結束位置。第一行與容器在交叉軸結束方向上的邊界保持對齊,其他行按照順序依次排列。
center 行集中於容器的中央。行都往容器的中央排列,在交叉軸起始方向和結束方向上留有一樣大小的空白空間。若是空白空間不足,則行會在兩個方向上超出一樣的空間。
space-between 行在容器中均勻分佈。第一行與容器在交叉軸起始方向上的邊界保持對齊,最後一行與容器在交叉軸結束方向上的邊界保持對齊。空白空間在行之間平均分配,使得相鄰行之間的空白尺寸相同。
space-around 相似於 space-between,不一樣的是第一行條目和最後一個行目與容器行的邊界之間一樣存在空白空間,而該空白空間的尺寸是行目之間的空白空間的尺寸的一半。
stretch 伸展行來佔滿剩餘的空間。多餘的空間在行之間平均分配,使得每一行的交叉軸尺寸變大。
圖 4. 屬性「align-content」的不一樣值的佈局效果

屬性「align-content」的不一樣值的佈局效果

 

應用示例

下面經過一個示例來具體說明彈性盒佈局在實際開發中的應用。該示例是一個博客帖子的典型頁面佈局。在展現一個博客帖子時,頁面上一般包括標題、發表者、日期和時間、評論數量、正文、插圖、評論列表等元素。這些元素基本上按照從上到下的順序依次排列。代碼清單 9清單 10中給出了示例的 HTML 和 CSS 代碼。預覽頁面見這裏

清單 9. 博客頁面的 HTML 代碼
<div class="post">
  <h1>This is my first blog post</h1>
  <div class="post-meta">
    <div class="author">Alex Cheng</div>
    <div class="datetime">2014-07-02 10:10 am</div>
    <div class="comments-count">2 comments</div>
  </div>
  <div class="post-body">
My first blog post.

  </div>
  <div class="post-image">
    <img src="http://placehold.it/500x200&text=1">
  </div>
  <div class="post-comments">
    <h3>Comments</h3>
    <ul>

<li><div class="author">Bob</div><div>This is a good post.</div></li>
 <li><div class="autho">David</div><div>Good post.</div></li>

    </ul>  
  </div>
</div>
清單 10. 博客頁面的 CSS 代碼
.post {
  display: flex;
  flex-flow: column wrap;
}
.post-meta {
  display: flex;
  flex-flow: row wrap;
  order: 1;
}
.post-body {
  order: 3;
}
.post-comments {
  order: 4;
}
.comments-count {
  margin-left: auto;
}
.post-image {
  order: 2;
  align-self: center;
}

該示例中主要使用了"order"屬性來改變條目的顯示位置,以及使用"align-self"來使得圖片居中顯示。

 

瀏覽器支持

因爲彈性盒模型規範自己有過多個不一樣的版本,所以瀏覽器對於該規範的支持也存在一些不一致。瀏覽器一共支持 3 個不一樣版本規範的語法:

  • 新規範:最新版本規範的語法,即"display: flex"。
  • 中間版本:2011 年的非官方規範的語法,即"display: flexbox"。
  • 老規範:2009 年的規範的語法,即"display: box"。

瀏覽器的支持狀況以下6所示。

表 6. 彈性盒佈局模型的瀏覽器支持
Chrome Safari Firefox Opera IE Android iOS
21+(新規範)
20-(老規範)
6.1+(新規範)
3.1+(老規範)
22+(新規範)
2-21(老規範)
12.1+(新規範) 11+(新規範)
10(中間版本)
4.4+(新規範)
2.1+(老規範)
7.1+(新規範)
3.2+(老規範)

6中能夠看到,彈性盒佈局模型已經被主流的瀏覽器所支持。不過爲了支持不一樣版本的瀏覽器,在使用時除了規範中定義的屬性以外,還須要添加相應的瀏覽器前綴形式,如代碼清單 11所示。

清單 11. 彈性盒佈局模型的瀏覽器前綴形式
.flex-container {
 display: -webkit-box;
 display: -moz-box;
 display: -ms-flexbox;
 display: -webkit-flex;
 display: flex;
}

.flex-item {
 -webkit-box-flex: auto;

-moz-box-flex: auto;
-webkit-flex: auto;
-ms-flex: auto;
 flex: auto;
}

.flex-item {
 -webkit-box-ordinal-group: 1;
 -moz-box-ordinal-group: 1;
 -ms-flex-order: 1;
 -webkit-order: 1;
 order: 1;
}

對於這些瀏覽器相關的前綴,最好使用 autoprefixer 這樣的工具來進行處理。

總結

做爲 CSS3 規範的一部分,彈性盒佈局模型能夠在不少典型的場景中簡化完成佈局所需的 CSS 代碼。該佈局模型也提供了不少實用的特性來知足常見的佈局要求,包括對容器中條目的排列、對齊、調整大小和分配空白空間等。彈性盒佈局模型能夠做爲 Web 開發人員工具箱中的一個很好的工具。

相關文章
相關標籤/搜索