CSS3 彈性盒子(Flexible Box 或 Flexbox),是一種用於在頁面上布置元素的布局模式,使得當頁面布局必須適應不同的屏幕尺寸和不同的顯示設備時,元素可預測地運行。對於許多應用程序,彈性盒子模型提供了對塊模型的改進,因為它不使用浮動,flex容器的邊緣也不會與其內容的邊緣折疊。
許多設計師會發現彈性盒子模型更易於使用。彈性盒子中的子元素可以在各個方向上進行布局,並且能以彈性尺寸來適應顯示空間。由於元素的顯示順序可以與它們在源代碼中的順序無關,定位子元素將變得更容易,並且能夠用更簡單清晰的代碼來完成復雜的布局。這種無關性是僅限制於視覺呈現上的,語言順序以及基於源代碼順序的導航均不受影響。
彈性盒布局概念
在定義方面來說,彈性布局是指通過調整其內元素的寬高,從而在任何顯示設備上實現對可用顯示空間最佳填充的能力。彈性容器擴展其內元素來填充可用空間,或將其收縮來避免溢出。
塊級布局更側重於垂直方向、行內布局更側重於水平方向,與此相對的,彈性盒子布局算法是方向無關的。雖然塊級布局對於單獨一個頁面來說是行之有效的,但其仍缺乏足夠的定義來支持那些必須隨用戶代理(user agent)不同或設備方向從水平轉為垂直等各種變化而變換方向、調整大小、拉伸、收縮的應用程序組件。 彈性盒子布局主要適用於應用程序的組件及小規模的布局,而(新興的)柵格布局則針對大規模的布局。這二者都是 CSS 工作組為在不同用戶代理、不同書寫模式和其他靈活性要求下的網頁應用程序有更好的互操作性而做出的更廣泛的努力的一部分。
彈性盒布局相關詞匯
關於彈性盒子的討論已經從諸如水平/行內軸和垂直/塊級軸這些術語中解放出來,與此同時,需要有一套新的術語來正確描述此模型。在學習下面的詞匯項目時請對照下圖。圖中是一個 flex-direction
屬性為 row
的彈性容器,意味着其內的彈性項目將根據既定書寫模式沿主軸水平排列,其方向為元素的文本流方向,在這個例子里,為從左到右。
- 彈性容器(Flex container)
-
包含着彈性項目的父元素。通過設置
display
屬性的值為flex
或inline-flex
來定義彈性容器。 - 彈性項目(Flex item)
-
彈性容器的每個子元素都稱為彈性項目。彈性容器直接包含的文本將被包覆成匿名彈性單元。
- 軸(Axis)
-
每個彈性框布局包含兩個軸。彈性項目沿其依次排列的那根軸稱為主軸(main axis)。垂直於主軸的那根軸稱為側軸(cross axis)。
flex-direction
確立主軸。justify-content
定義了在當前行上,彈性項目沿主軸如何排布。align-items
定義了在當前行上,彈性項目沿側軸默認如何排布。align-self
定義了單個彈性項目在側軸上應當如何對齊,這個定義會覆蓋由align-items
所確立的默認值。
- 方向(Direction)
-
彈性容器的主軸起點(main start)/主軸終點(main end)和側軸起點(cross start)/側軸終點(cross end)描述了彈性項目排布的起點與終點。它們具體取決於彈性容器的主軸與側軸中,由
writing-mode
確立的方向(從左到右、從右到左,等等)。order
屬性將元素與序號關聯起來,以此決定哪些元素先出現。flex-flow
屬性是flex-direction
和flex-wrap
屬性的簡寫,決定彈性項目如何排布。
- 行(Line)
-
根據
flex-wrap
屬性,彈性項目可以排布在單個行或者多個行中。此屬性控制側軸的方向和新行排列的方向。 - 尺寸(Dimension)
-
根據彈性容器的主軸與側軸,彈性項目的寬和高中,對應主軸的稱為主軸尺寸(main size) ,對應側軸的稱為 側軸尺寸(cross size)。
min-height
與min-width
屬性初始值將為 0。flex
屬性是flex-grow
、flex-shrink
和flex-basis
屬性的簡寫,描述彈性項目的整體的伸縮性。
定義一個彈性盒子
為要使用此樣式的元素指派 CSS,需按以下方式設置 display 屬性:
display : flex
或者
display : inline-flex
這樣做將元素定義為彈性容器,其子元素則成為彈性項目。值 flex
使彈性容器成為塊級元素。值 inline-flex
使彈性容器成為單個不可分的行內級元素。
display : -webkit-flex
。
彈性項目須知
彈性容器直接包含的文本將自動包覆成匿名彈性項目。不過,一個只包含一系列空白符(如一堆空格或制表符等)的匿名彈性項目不會被渲染,就如同對其指派 display: none
。
對於彈性容器的絕對定位子元素來說,其靜態位置參照彈性容器的內容框的主起始角確定,而后依此完成此元素的定位。
相鄰的彈性元素其外邊距不會互相合並。使用 auto
外邊距可以吸收掉水平或垂直方向上的額外空間,這可以用於對齊或分隔相鄰的彈性項目。更多細節請參考 W3C 彈性框布局模型規范中的 Aligning with 'auto' margins。
不像 CSS 中的其他對齊方法,彈性框的對齊屬性將進行“真正的”居中對齊。這意味着即使彈性條目溢出了彈性容器,它依然保持居中。不過這在某些時候可能會有問題。如果溢出超過了頁面的上邊緣或左邊緣(在從左到右的語言中,比如英語;在諸如阿拉伯語這樣從右到左的語言中這個問題更會出現在右邊緣),則雖然那些地方確實有內容,卻無法滾動到那些位置。在未來的發布版本里,對齊屬性將會有所擴展,使其包含有“安全”選項。目前,如果操心這點,可以改用外邊距來達成居中效果,因為外邊距會用比較“安全”的方式來響應變化,出現溢出時將停止居中。對這種需要居中的彈性項目,不使用 align-
屬性,而使用自動外邊距就能解決這個問題。對彈性容器中第一個和最后一個彈性項目的外側邊緣應用,也可以使用自動外邊距來替代 justify-
屬性。自動外邊距會自動伸縮來占滿剩余空間,當有剩余空間存在時彈性項目將會居中,如果沒有則切換至常規對齊方式。不過很不幸,如果嘗試在多行的彈性框中用基於外邊距的居中方法來替代 justify-content
,就必須對每一行的第一個和最后一個彈性項目應用外邊距。此時除非能夠事先預測每一行都結束於哪個元素,否則就不能愉快的在主軸方向上用基於外邊距的居中方法來替代 justify-content
屬性了。
再強調一遍,元素的顯示順序與它們在源代碼中的順序無關,這種無關性只影響視覺呈現,語音順序以及基於源代碼順序的導航均不受影響。order
屬性並不影響語音和導航的次序。因此開發者們必須小心,合理地安排元素在源代碼中的順序,以免破壞文檔的可訪問性。
彈性盒子相關屬性
不影響彈性盒子的屬性
由於彈性盒子使用了不同的布局算法,某些屬性用在彈性容器上沒有意義:
- 多欄布局模塊的
column-*
屬性對彈性項目無效。 float
與clear
對彈性項目無效。使用float
將使元素的display
屬性計為block
。vertical-align
對彈性項目的對齊無效。
示例
基本的彈性布局示例
這個基本的示例展示了如何對元素應用彈性布局,以及在彈性布局狀態下相鄰元素的行為方式。
<!DOCTYPE html> <html lang="en"> <head> <style> .flex { /* 基本樣式 */ width: 350px; height: 200px; border: 1px solid #555; font: 14px Arial; /* 建立彈性框 */ display: -webkit-flex; -webkit-flex-direction: row; display: flex; flex-direction: row; } .flex > div { -webkit-flex: 1 1 auto; flex: 1 1 auto; width: 30px; /* 讓過渡表現良好。(從/到"width:auto"的過渡 至少在 Gecko 和 Webkit 上是有 bug 的。 更多信息參見 http://bugzil.la/731886 ) */ -webkit-transition: width 0.7s ease-out; transition: width 0.7s ease-out; } /* colors */ .flex > div:nth-child(1){ background : #009246; } .flex > div:nth-child(2){ background : #F1F2F1; } .flex > div:nth-child(3){ background : #CE2B37; } .flex > div:hover { width: 200px; } </style> </head> <body> <p>Flexbox nuovo</p> <div class="flex"> <div>uno</div> <div>due</div> <div>tre</div> </div> </body> </html>
聖杯布局示例
此示例展示了彈性盒子根據不同屏幕分辨率動態改變布局的能力。下圖說明了這種轉換。
這里展示的正是針對瀏覽器窗口的頁面布局必須為智能手機窗口優化的場景。不僅元素的尺寸需要縮減,其呈現順序也需要改變。彈性盒子讓這變得很簡單。
<!DOCTYPE html> <html lang="en"> <head> <style> body { font: 24px Helvetica; background: #999999; } #main { min-height: 800px; margin: 0px; padding: 0px; display: -webkit-flex; display: flex; -webkit-flex-flow: row; flex-flow: row; } #main > article { margin: 4px; padding: 5px; border: 1px solid #cccc33; border-radius: 7pt; background: #dddd88; -webkit-flex: 3 1 60%; flex: 3 1 60%; -webkit-order: 2; order: 2; } #main > nav { margin: 4px; padding: 5px; border: 1px solid #8888bb; border-radius: 7pt; background: #ccccff; -webkit-flex: 1 6 20%; flex: 1 6 20%; -webkit-order: 1; order: 1; } #main > aside { margin: 4px; padding: 5px; border: 1px solid #8888bb; border-radius: 7pt; background: #ccccff; -webkit-flex: 1 6 20%; flex: 1 6 20%; -webkit-order: 3; order: 3; } header, footer { display: block; margin: 4px; padding: 5px; min-height: 100px; border: 1px solid #eebb55; border-radius: 7pt; background: #ffeebb; } /* 窄到已不足以支持三欄 */ @media all and (max-width: 640px) { #main, #page { -webkit-flex-flow: column; flex-direction: column; } #main > article, #main > nav, #main > aside