Flex Layout 詳解
總想寫flex,卻一直拖——拖——拖,今天算是寫完了。與以往的文章不同,這篇文章更像是寫給自己看的,所以有些術語不會再解釋,如看不懂,可以先去這篇文章裡看看術語.
Flex Container
-
display為flex的元素會產生一個flex container,該container在正常流內以block-level的形式存在。
-
display為inline-flex的元素會產生一個flex container,該container在正常流內以inline-level的形式存在。
-
每個flex container都會為它的內容建立一個flex formatting context,這與block container為它的內容建立一個block formatting context很類似。但是,flex container並不是block container, 諸如:
-
float和clear在flex container裡面無效(也就是flex item即使設定了float和clear也不起作用)
-
vertical-align在flex item上也不起作用
-
flex container的margin不會與它的內容的margin重疊
-
::first-line和::first-letter偽元素在flex container上不起作用
Specified value | Computed value |
---|---|
inline-table | table |
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-block | block |
inline-flex | flex |
others | same as specified |
Flex Item
簡單的說,flex container的每個孩子都是一個flex item,而每個flex item都是一個flex-level box,另外,由於在計算computed value時flex item會發生塊級化,因此每個flex item也是一個block container。而對於沒有html元素包裹的連續文字,會被自動包裹在一個匿名的block container flex item裡。
釋1: 塊級化 —— 儘管有的flex item的display屬性值為inline,但是在計算computed value時,也會被設為block.
釋2: block container ——該box只能包含block-level box或是建立一個inline formatting context且只能包含inline-level box
1. flex item為絕對定位元素
因為為絕對定位元素,所以該flex item會脫離流。效果就如同該flex item的static position box(在計算絕對定位元素位置時,會先假設它的position為static,處於正常流中,然後得出一個static position,再依據這個static position去定位。)為flex container的content box。也就是該flex container裡面只有該flex item的static position box,這個flex container是個匿名的flex container。
例1:
div.parent { display: flex; width: 1000px; height:400px; border: solid 2px rgba(75, 234, 12, 0.7); border-radius: 8px; } div.parent > div { border-radius: 8px; flex: 1 1 auto; color: #fff; text-align: center; line-height: 200px; font-size: 26px; } div.first-child { width: 200px; height: 200px; background-color: rgb(64, 166, 249); } div.second-child { width: 400px; height: 200px; background-color: rgb(255, 141, 75); } 複製程式碼
<div class="parent"> <div class="first-child">first-child</div> <div class="second-child">second-child</div> </div> 複製程式碼
效果如下:

現在為div.first-child加上絕對定位。div.first-child改為:
div.first-child { Width: 200px; height: 200px; background-color: rgb(64, 166, 249); position: absolute; } 複製程式碼
效果如下:

選中div.second-child可以看到,它的寬度獨佔1000px。

如果給絕對定位flex item加上align-self: center,它會處於它所在的flex container的交叉軸的中心位置。修改div.first-child如下:
div.first-child { width: 200px; height: 200px; background-color: rgb(64, 166, 249); position: absolute; align-self: center; } 複製程式碼
效果如下:

而一旦為絕對定位flex item設定了top/bottom,它就會參照第一個父級定位元素去移動,align-self也就失效了。現在設定div.parent為相對定位元素,修改成:
div.parent { display: flex; Width: 1000px; height:400px; border: solid 2px rgba(75, 234, 12, 0.7); border-radius: 8px; position:relative; } 複製程式碼
為div.first-child設定偏移量,修改如下:
div.first-child { width: 200px; height: 200px; background-color: rgb(64, 166, 249); position: absolute; align-self: center; left: 50px; top: 50px; } 複製程式碼
效果如下:

對比上圖,可以看到交叉軸上div.first-child距離div.parent的上邊緣只有上圖的一半,也就是50px,同時div.first-child距離div.parent的左邊緣也是50px。
2. flex item的margin和padding
- 相鄰的flex item的margin不會重疊(flex container的margin也不會與flex item的margin重疊)。
- margin和padding若設定為百分比,則百分比是基於flex container的inline size(若writing mode是horizontal,則inline size為寬度;若mode是vertical,則inline size 為高度 )。
例子:
div.parent { display: flex; flex-wrap: wrap; width: 400px; height: auto; background-color: rgba(15, 241, 170, 0.42); border-radius: 8px; margin-top: 50px; } div.parent > div { border-radius: 8px; flex: 1 1 auto; color: #fff; text-align: center; line-height: 200px; font-size: 26px; } div.first-child { width: 200px; height: 200px; background-color: rgb(64, 166, 249); margin-top: 50px; margin-bottom: 50px; } div.second-child { width: 400px; height: 200px; background-color: rgb(255, 141, 75); margin-top: 50px; } 複製程式碼
效果如下:

可以看到div.parent距離root box的上邊緣有50px,而div.first-child的上邊緣距離div.parent的上邊緣也有50px,他們並沒有重疊。div.first-child及div.second-child的margin-top也沒有重疊。
如果div.parent的display為block,並且div.parent及div.first-child的border寬度均為0,它和div.first-child的margin-top會發生重疊。修改div.parent如下:
div.parent { display: block; width: 400px; height: auto; background-color: rgba(15, 241, 170, 0.42); border-radius: 8px; margin-top: 50px; } 複製程式碼
效果如下:

滑鼠定位到div.parent上,可以看到它的margin-top和div.first-child的margin-top確實重疊了。同理,div.first-child與div.second-child的margin-top也重疊了。

3. 摺疊的flex item
- Flex item可以設定visibility: collapse,達到摺疊的效果。效果類似於table的行摺疊。
- Flex item即使摺疊了,但依然有個隱形的空殼存在,為的是保證flex container的cross size的穩定。如果flex container僅有一個flex line(每條flex line上按main size方向依次擺放flex item,類似於line box),那麼某個flex item摺疊可能會影響flex container 的main size,但是不會影響flex container的cross size。
- 儘管摺疊的flex item不會被渲染,但依然會出現在dom 樹上。為了計算摺疊的flex item的那個空殼的大小,在dom樹形成的過程中,flex layout 會先假設所有item都沒有摺疊,然後在由dom樹形成渲染樹的過程中,會將摺疊的flex item用一個保留了原始的cross size的空殼替換掉。
例子:
div.collapse-example { display: flex; > ul.nav { height: auto; margin: 0; margin-right: 20px; > li { margin-bottom: 5px; border-radius: 4px; text-align: center; font-size: 18px; display: flex; flex-flow: column; } > li:target, > li:hover { cursor: pointer; > a { border-top-left-radius: 8px; border-top-right-radius: 8px; font-weight: bold; } > ul { border-radius: 8px; } a { color: #343434 !important; } } > li:not(:target):not(:hover) > ul { height: 0; overflow: hidden; visibility: collapse; } } ul { padding: 0 10px; > li { list-style: none; } } } 複製程式碼
<div className="collapse-example"> <ul class="nav"> <li> <a href="#">About</a> <ul> <li> <a href="#">introduction</a> </li> <li> <a href="#">blog</a> </li> </ul> </li> <li> <a href="#">Product</a> <ul> <li> <a href="#">book</a> </li> <li> <a href="#">beautiful-closes</a> </li> </ul> </li> <li> <ahref="#">Contact</a> <ul> <li> <a href="#">email</a> </li> <li> <a href="#">weixin</a> </li> </ul> </li> </ul> <article id="main"> This is an Example </article> </div> 複製程式碼
效果如下:

滑鼠移到選單上,子選單會顯示出來
4. flex item的automatic minimum size
min-width和min-height有了一個新的初始化值,即auto,表示自動設定的min size。
對於flex item,當min-width或min-height的specified value設定為auto,相應的used value是基於內容來設定的。這個值怎麼定呢?
對於min-width,一般,取主軸上width的specified value與主軸上min-content size(一個box的最小size,該size不會導致overflow)的較小值; 如果主軸上width沒有specified value, 但是有個ratio且交叉軸上width設定了specified value,則根據ratio及交叉軸width的specified value得出一個轉換後的size,再取這個size和主軸上min-content size的較小值;如果既沒有設定主軸width的specified value有沒有ratio,那麼直接取主軸上min-content size。
同理min-height。
通常,automatic minimum size取值合理,但是如果是以下情況,還是為min size設定一個具體的值比較好。
- 當flex item的content是一篇較大的文件,給min-width或min-height設定成字型相關的值比較好,例如min-width: 12em。使用 automatic minimum size會導致溢位。
- 當flex item有較多的孩子節點時,automatic minimum size的取值需要基於min-content size, 而 layout engine為了找到min-content size需要一一遍歷所有的孩子節點,有損效能。如果直接給min size設定specified value,就不必去找min-content size了。
注意:ratio表示一個元素的width與height的比例關係,一般通過給padding-top設定百分比來表達一個固定比例。
aspect ratio | padding-top value |
---|---|
1:1 | 100% |
16:9 | 56.25% |
4:3 | 75% |
3:2 | 66.66% |
8:5 | 62.5% |
定向和排序
flex container的內容可以按照一定的方向和順序排列。主要依靠flex container的flex-direction和flex-wrap以及flex item的order。
但是有點需切記,不可依靠flex-direction/flex-wrap 的-reverse值 以及order來代替flex item的原本順序,會影響頁面的可訪問性。
1. flex-direction
Name: flex-directionValue: row | row-reverse | column | column-reverseInitial: rowApplies to: flex containersInherited: noPercentages: n/a
2. flex-wrap
Name: flex-wrapValue: nowrap | wrap | wrap-reverseInitial: nowrapApplies to: flex containersInherited: noPercentages: n/a
flex: nowrap表示flex container為單行,內容大小超出則調整flex item的大小以免溢位。

flex: wrap 表示在內容過多時flex container會換行

flex: wrap-reverse 表示在內容過多時flex container會換行,但cross-start及cross-end方向調換。

3. Order
Name: orderValue:Initial: 0Applies to: flex itemsInherited: noPercentages: n/a
order可以設定flex item的位置。 這些flex item會在文件順序的基礎上再基於order屬性值進行再排序,然後按照再排序的結果渲染出來。如果某些flex item的order值相同,則按照它們在文件裡的順序渲染。
例子:
div.parent { display: flex; width: 400px; height: auto; border: solid 2px rgba(15, 241, 170, 0.42); border-radius: 8px; } div.parent > div { border-radius: 8px; flex: 1 1 auto; color: #fff; text-align: center; line-height: 200px; font-size: 26px; } div.first-child { width: 200px; height: 200px; background-color: rgb(64, 166, 249); } div.second-child { width: 400px; height: 200px; background-color: rgb(255, 141, 75); order: -1; } 複製程式碼
效果如下:

靈活性
1. flex-grow
當一個flex line還有多餘的空間可擴充套件,這些空間如何分配給該行的flex item呢?用flex-grow, 它用來設定某個flex item可擴充套件多少比例的多餘空間。取值為number,預設為0。
2. flex-shrink
當一個flex line的空間不足,這些缺失的空間如何分擔給該行的flex item呢?用flex-shrink,它表示某個flex item需要縮小多少比例的空間,取值為number,預設為1。
3. flex-basic
定義了分配多餘空間(可為正數亦可為負數)前該flex item所佔的main size,瀏覽器會根據該值來計算主軸上的多餘空間。可設定如下屬性值:
- auto: 當specified value為auto時,按如下步驟取值。 第一步:採用的used value為flex item的main size(即主軸上的width或height的屬性值), 第二步:如果這個used value依然為auto,那麼used value 會基於 flex item的content 得到一個具體值。
- content: 基於flex item的content得到一個automatic size。
- <width>: 和width或height的設定方式一樣,可設為<length> 或 <percentage>
預設值為auto。當主軸為水平方向,設定了flex-basic,則flex item的width值會失效。例如,如果某個flex item的flex-basic設為0,則把該flex item的寬度視為0,即使它本身width為100px,這個100px也會被納入多餘空間中,供flex inline的所有flex item一起分配。
4. flex
flex屬性為flex-grow flex-shrink flex-basic的縮寫,預設值為0 1 auto,可設定的值為:
- initial: 0 1 auto,當有多餘空間時,沒有彈性;當空間不足時,flex item可以縮小。
- auto: 1 1 auto,flex會有充足的彈性
- none: 0 0 auto, flex會完全沒有彈性
- <positive-number>: 1 auto
- <flex-grow> <flex-shrink> <flex-basic>: 若flex-grow省略了,預設值取1;若flex-shrink省略了,預設值取1;若flex-basic省略了,預設值取0。
通常,flex item不會縮小到比min content sizes還要小。為防萬一,我們可以為flex item設定min-width或min-height。
對齊
1. 使用auto margin對齊
- 如果設定了flex-grow,auto margin 會被設定為0;若設定了flex-basis,沒有設定flex-grow,那麼在主軸方向上,根據flex-basis計算出的多餘的空間都會分配給auto margin
- auto margin的優先順序高於justify-content及align-self,軸上任何多餘的空間(不包括負的空間)都會分配給auto margin。
- 如果box在某個軸上發生溢位了,則auto margin會被忽略,且box在該軸的尾部溢位。(如果既有auto margin, 又有justify-content/align-self/align-items,依然是忽略justify-content/align-self/align-items,採用auto margin,只不過此時因為overflow,接著會忽略auto margin, 在軸的尾部溢位)
總的來說,當存在auto margin時,又有多餘的空間(不包括負的空間),則優先順序如下:
flex-grow > auto margin > justify-content/align-self/align-items
如果box在軸上只剩下負的空間(即溢位),則auto margin被忽略。
例子:
div.parent { display: flex; flex-wrap: nowrap; justify-content: center; width: 1000px; height: auto; border: solid 5px rgba(15, 241, 170, 0.42); border-radius: 8px; } div.parent > div { border-radius: 8px; flex: 1 1 auto; color: #fff; text-align: center; line-height: 200px; font-size: 26px; } div.first-child { width: 200px; height: 200px; background-color: rgb(64, 166, 249); margin-left: auto; } div.second-child { width: 400px; height: 200px; background-color: rgb(255, 141, 75); } 複製程式碼
現在div.parent的justify-content設為center, div.first-child及div.second-child設定了flex: 1 1 auto(其中flex-grow: 1, flex-shrink: 1, flex-basis: auto); 同時div.first-child設定了margin-left為auto,效果如下:

可以看到div.first-child及div.second-child均擴充套件了,justify-content及auto margin沒起作用。
現在設定div.first-child及div.second-child的flex為0 1 auto,修改如下:
div.parent > div { border-radius: 8px; flex: 0 1 auto; color: #fff; text-align: center; line-height: 200px; font-size: 26px; } 複製程式碼
效果如下:

可以看到,剩餘空間都被div.first-child的auto margin-left 佔據了。
現在去掉div.first的auto margin,修改如下:
div.first-child { width: 200px; height: 200px; background-color: rgb(64, 166, 249); } 複製程式碼
效果如下:

現在,justify-content起作用了。
2.justify-content對齊
justify-content用來設定flex-item在主軸上的對齊方式。
Name: justify-contentValue: flex-start | flex-end | center | space-between | space-aroundInitial: flex-startApplies to: flex containersInherited: no
效果如下:

3. align-items和align-self
align-items定義了flex container中flex-item在交叉軸上的對齊方式,有點類似justify-content,是針對所有flex item。
align-self定義了某個flex item在交叉軸上的對齊方式,會覆蓋align-items的值。
Name: align-itemsValue: flex-start | flex-end | center | baseline | stretchInitial: stretchApplies to: flex containersInherited: no
Name: align-selfValue: auto | flex-start | flex-end | center | baseline | stretchInitial: autoApplies to: flex itemsInherited: no
- auto: 對於flex-item,可以設定align-self為auto, auto值的computed value會設定為flex container的align-items的值。
- center: flex item的margin box 在交叉軸方向上處於flex line的中間位置。如果flex line的高度比flex item的還要低,則flex item在交叉軸的首尾兩端溢位相等部分。
- baseline: flex item會依照flex line的baseline對齊。在交叉軸方向上,baseline與flex item的margin box的上邊緣距離最遠的那個flex item會被放置在交叉軸的開始位置。
- stretch: 若flex item的 cross size的computed value為auto(如果flex-direction為row,那麼cross size就是flex item的height),且交叉軸方向的margin均不為auto,那麼align-items/align-self為stretch時,flex item會被拉伸。flex item的cross size的used value會盡可能的接近flex line的高度,如果flex item設定了min(max)-width(height),那麼這個used value會受它們的限制。
效果如下圖:

4. align-contents
Name: align-contentValue: flex-start | flex-end | center | space-between | space-around | stretchInitial: stretchApplies to: multi-line flex containersInherited: no
align-content定義了flex line在flex container的交叉軸方向上的對齊方式,類似於justify-content,都屬於flex container屬性。只不過它要對齊的物件是flex line,且在交叉軸方向,而justify-content要對齊的物件是flex item,在主軸上。
屬性值如下:
space-around: flex container中的flex line之間的空間相等,第一個flex line與 flex container的margin box的上邊緣之間的空間只有flex line之間的空間的一半。
效果如下:
