Flex Layout 詳解

總想寫flex,卻一直拖——拖——拖,今天算是寫完了。與以往的文章不同,這篇文章更像是寫給自己看的,所以有些術語不會再解釋,如看不懂,可以先去這篇文章里看看術語


Flex Container

1. display為flex的元素會產生一個flex container,該container在正常流內以block-level的形式存在。

2. display為inline-flex的元素會產生一個flex container,該container在正常流內以inline-level的形式存在。

3. 每個flex container都會為它的內容建立一個flex formatting context,這與block container為它的內容建立一個block formatting context很類似。但是,flex container並不是block container, 諸如:

1) float和clear在flex container裡面無效(也就是flex item即使設置了float和clear也不起作用)

2) vertical-align在flex item上也不起作用

3) flex container的margin不會與它的內容的margin重疊

4) ::first-line和::first-letter偽元素在flex container上不起作用

注意:當元素為絕對定位元素、浮動元素或根元素時,display屬性值的computed value的計算規則參照下表

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:

css 代碼
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);
}
html 代碼
<div class="parent">
<div class="first-child">first-child</div>
<div class="second-child">second-child</div>
</div>

效果如下:

例2:為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。

例3: 如果給絕對定位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;
}

效果如下:

例4:而一旦為絕對定位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 為高度)。

例5:

css代碼
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;
}

html代碼
<div class="parent">
<div class="first-child">first-child</div>
<div class="second-child">second-child</div>
</div>

效果如下:

可以看到div.parent距離root box的上邊緣有50px,而div.first-child的上邊緣距離div.parent的上邊緣也有50px,他們並沒有重疊。div.first-child及div.second-child的margin-top也沒有重疊。

例6:如果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的那個空殼的大小,flex layout會先假設所有item都沒有摺疊(處於dom樹形成的過程中,specified value變computed value),然後在由dom樹形成渲染樹的過程中,會將摺疊的flex item用一個保留了原始的cross size的空殼替換掉。

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設置一個具體的值比較好。1. 當flex item的content是一篇較大的文檔,給min-width或min-height設置成字體相關的值比較好,例如min-width: 12em。使用 automatic minimum size會導致溢出。2. 當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設定百分比來表達一個固定比例。

定向和排序

flex container的內容可以按照一定的方向和順序排列。主要依靠flex container的flex-direction和flex-wrap以及flex item的order。

但是有點需切記,不可依靠flex-direction/flex-wrap 的-reverse值 以及order來代替flex item的原本順序,會影響頁面的可訪問性。

1. flex-direction

Name: flex-direction

Value: row | row-reverse | column | column-reverse

Initial: row

Applies to: flex containers

Inherited: no

2. flex-wrap

Name: flex-wrap

Value: nowrap | wrap | wrap-reverse

Initial: nowrap

Applies to: flex containers

Inherited: no

flex: nowrap表示flex container為單行,內容大小超出則調整flex item的大小以免溢出

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

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

3. order

Name: order

Value: <integer>

Initial: 0

Applies to: flex items

Inherited: no

order可以設置flex item的位置。 這些flex item會在文檔順序的基礎上再基於order屬性值進行再排序,然後按照再排序的結果渲染出來。如果某些flex item的order值相同,則按照它們在文檔里的順序渲染。

例7:

css代碼
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;
}

html代碼
<div class="parent">
<div class="first-child">first-child</div>
<div class="second-child">second-child</div>
</div>

效果如下:

靈活性

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>: <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對齊

a) 如果設置了flex-grow,auto margin 會被設置為0;若設置了flex-basis,沒有設置flex-grow,那麼在主軸方向上,根據flex-basis計算出的多餘的空間都會分配給auto margin)

b) auto margin的優先順序高於justify-content及align-self,軸上任何多餘的空間(不包括負的空間)都會分配給auto margin。

c) 如果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被忽略。

例8:

css代碼
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);
}

html代碼
<div class="parent">
<div class="first-child">first-child</div>
<div class="second-child">second-child</div>
</div>

現在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沒起作用。

例9:現在設置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 佔據了。

例10:現在去掉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-content

Value: flex-start | flex-end | center | space-between | space-around

Initial: flex-start

Applies to: flex containers

Inherited: no

效果如下:

3. align-items和align-self

align-items定義了flex container中flex-item在交叉軸上的對齊方式,有點類似justify-content,是針對所有flex item。

align-self定義了某個flex item在交叉軸上的對齊方式,會覆蓋align-items的值。

Name: align-items

Value: flex-start | flex-end | center | baseline | stretch

Initial: stretch

Applies to: flex containers

Inherited: no

Name: align-self

Value: auto | flex-start | flex-end | center | baseline | stretch

Initial: auto

Applies to: flex items

Inherited: 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-content

Value: flex-start | flex-end | center | space-between | space-around | stretch

Initial: stretch

Applies to: multi-line flex containers

Inherited: 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之間的空間的一半。

其他屬性值直接看下面的效果圖

註明:本篇文章中少數圖片來源網路,如有侵權,請立即聯繫本人刪除。


推薦閱讀:

TAG:CSS布局 | Layout | CSS |