前言
最近經常用到 flex box, 但又常常看完把問題解決了, 過幾天要用時又忘了. 雖然網路上也有其他人整理的, 但總是這篇少這個, 那篇又有些設定沒說清楚, 最慘的是有些還弄錯了, 最後只好自己花時間整理.
目前相容性較高的 flex 語法大約是依據 2012 年的 "CSS Flexible Box Layout Module W3C Candidate Recommendation" 這一份文件. 比這個新的文件當然還有好幾份, 但是當中大部份的新設定目前 (2020/03) 幾乎都只有 1 個 browser 支援 (FireFox, 一小部份功能 Chrome 也有支援). 因此, 這裡還是以 2012 版的功能為主, 至少絕大部份 browser 都支援的, 用戶端不至於出現大問題.
flex 快速設定
快速設定: item 橫著排, 只要一行. (由左至右, 由上至下安置)
.flex-row {
display: flex;
}
快速設定: item 橫著排, item 順序倒著排 (由右至左, 由下至上安置).
.flex-row {
display: flex;
flex-flow: row-reverse wrap-reverse;
}
快速設定: item 直的排, 只要二行.
.flex-col {
display: flex;
flex-flow: column;
}
其他的, 依著需要再慢慢查, 慢慢調整.
flex 相關概念
- flex 是應用於區塊式內容的排版 (布局) 方式. flex 的設定可以分為二大部份:
- 套用於 container (容器, 外部區塊) 的設定項, 和
- 套用於 items (內容項, 內部區塊) 的設定項.
- flex 主要的設定項都在 container 區塊上, 大部份狀況下只要設定 container 即可. 而用在 items 的設定項大部份狀況是為了個別調整 item 的佈放條件.
flex-direction: row 的主軸、交錯軸及他們的啟始端和結束端
- 一個 flex container區塊內有二個軸向: Main Axis (主軸) 和 Cross Axis (交錯軸). 二個軸向各自有一個啟始端 (flex-start)和結束端 (flex-end). 指定 flex 主軸方向 (flex-direction) 時, 即為指定主軸為 '水平' 或 '垂直' 及它的啟始點 (和結束點) 的位置, 同時也指定另一軸為交錯軸(註一):
注意: 二者之間並沒有互斥關係, 可以同時運用在同一個 HTML tag 上. container 設定項是當這個 HTML tag 作為容器時用的; 而 items 設定項是當這個 HTML tag 作為上層 flex 容器的內容項時用的. 在需要多層套疊 flex 排版時一般都會遇到這種狀況.
- flex-direction:row: 以水平軸為主軸, 交錯軸為垂直 (由上至下)(註二)
主軸的啟始端 (flex-start) 在左, 結束端 (flex-end) 在右 - flex-direction:row-reverse: 以水平軸為主軸, 交錯軸為垂直 (由上至下)
主軸的啟始端 (flex-start) 在右, 結束端 (flex-end) 在左 - flex-direction:column: 以垂直軸為主軸, 交錯軸為水平 (由左至右)(註二)
主軸的啟始端 (flex-start) 在上, 結束端 (flex-end) 在下 - flex-direction:column-reverse: 以垂直軸為主軸, 交錯軸為水平 (由左至右)
主軸的啟始端 (flex-start) 在下, 結束端 (flex-end) 在上
註一: 主軸並不等於水平軸, flex-direction: row 才是把主軸指定為水平軸. 部份網站的圖片及說明搞錯了 (包括部份 google search 排名在很前面的繁中部落格), 請小心.
註二: 想要指定交錯軸為相反方向? 請使用flex-wrap:wrap-reverse.
flex 可以非常方便的應用在 RWD 自適應網頁設計上, 不必費太多心思在調整不同的設備的展現效果, 因此很受歡迎. 以常見的橫排效果來說, 一般可以先固定內容項的最小寬度, 而容器的寬度則依照 browser 或者設備的寬度改變 (直向或橫向). 於是當容器的寬度放不下內容項時, 便自動折返, 然後剩下的橫向空白平均分給這一排當中的各個內容項.
Container 設定項
- 啟動開關: 使用 flex 排版的必要設定.
display設為 flex 或 inline-flex 時啟動 flex 排版功能; 不是這二個關鍵字就會關閉 flex 排版功能, 並忽略相關的 flex 設定項 (包含 container 和它的下層 items).display: flex; /* 或者 */ display: inline-flex;
- 主軸流向與折返: flex-flow 是一個合併設定項, 它合併接下來要說明的 flex-direction 和 flex-wrap 等二項設定.
flex-flow: <'flex-direction'> <'flex-wrap'> /* (註三) */
註三: <'flex-direction'> 和 <'flex-wrap'> 沒有要求先後次序. 使用預設值的部份也可以不寫. 二個都想省略時則應該是不用設定 flex-flow 本身.
- 主軸流向: 設定 flex 容器內部內容項排版的流動方向. (預設值為 row)
flex-direction: row; /* 由左至右, 水平方向排列 */ flex-direction: row-reverse; /* 由右至左, 水平方向排列 */ flex-direction: column; /* 由上至下, 垂直方向排列 */ flex-direction: column-reverse; /* 由下至上, 垂直方向排列 */
下列 4 張圖片所展示的是 4 種 flex-driection 選項, 搭配 flex-wrap: wrap 的排版結果.
flex-direction: row
flex-direction: row-reverse
flex-direction: column
flex-direction: column-reverse
- 主軸折返: 內容項太多, 容器主軸一列 (或者一行) 放不下時, 是否要折返. (預設值為 nowrap)
flex-wrap: nowrap; /* 不折返 (註四) */ flex-wrap: wrap; /* 折返 */ flex-wrap: wrap-reverse; /* 折返, 同時 '交錯軸' 為 reverse (註五) */
註四: 設定 flex-wrap: nowrap 經常要搭配 flex-shrink: 0 才會使內容項會超出容器的範圍, 超出部份依容器的 overflow 設定處理. 但預設的 flex-shrink: 1 設定可以收縮各內容項進而使 overflow 不發生.
註五: wrap-reverse 是設定主軸要折返, 同時 '交錯軸' 為 reverse. 會這樣設計是因為主軸有折返時, 交錯軸的方向才有重大義意. 所以不要誤會它的意思為主軸逆向折返.
下面來看 flex-direction 和 flex-wrap 的 12 種組合:
- flex-direction: row 和 flex-wrap 的組合如下列三圖. 請注意 wrap 和 wrap-reverse 這二張圖的佈放位置為上下鏡射.
flex-flow: row nowrap
flex-flow: row wrap
flex-flow: row wrap-reverse
- flex-direction: row-reverse 和 flex-wrap 組合的佈放位置剛好是上列三張圖的左右鏡射.
flex-flow: row-reverse nowrap
flex-flow: row-reverse wrap
flex-flow: row-reverse wrap-reverse
- flex-direction: column 和 flex-wrap 的組合如下列三圖. 請注意 wrap 和 wrap-reverse 這二張圖的佈放位置為左右鏡射.
flex-flow: column nowrap
flex-flow: column wrap
flex-flow: column wrap-reverse
- flex-direction: column-reverse 和 flex-wrap 組合的佈放位置剛好是上列三張圖的上下鏡射.
flex-flow: column-reverse nowrap
flex-flow: column-reverse wrap
flex-flow: column-reverse wrap-reverse
- flex-direction: row 和 flex-wrap 的組合如下列三圖. 請注意 wrap 和 wrap-reverse 這二張圖的佈放位置為上下鏡射.
- 主軸排版對齊 (空白控制): 內容項在主軸方向上排列完之後, 如何處理可能多出來的空白(註六). (預設值為flex-start)
justify-content: flex-start; /* 靠主軸啟始端, 空白留在結束端 */ justify-content: flex-end; /* 靠主軸結束端, 空白留在啟始端 */ justify-content: center; /* 主軸居中, 空白留在二端 */ justify-content: space-between; /* 主軸空白平均分配於內容項之間, 二端不留空白 */ justify-content: space-around; /* 主軸空白平均分配給各內容項二端 (註七) */
註六: 內容項的flex-grow設定優先於這個設定. 也就是如果有flex-grow設定值不為 0 的內容項它就會把這些個空白吃掉 (變成沒有多餘的空白, 或者也可以控制要吃掉多少比例).
註七: space-around是先將空白平均分配給各內容項, 再各以 50% 分配給內容項的二側 (主軸為水平時), 或上下 (主軸為垂直時). 所以位在二端的空白要比中間的空白要小一半.
下列五張圖是設定 flex-flow: row wrap 再加上 justify-content 五種不同的設定值的排版結果. 由於組合太多了, 其他的麻煩各位自行腦補, 這裡就不貼了.
justify-content: flex-start
justify-content: flex-end
justify-content: center
justify-content: space-between
justify-content: space-around
Part 2:
還在 2020 草案中的新設定項, 目前大部份 browser 都還未支援.所有主流 browser 均已支援.justify-content: start; /* 靠 writing-mode 的啟始端 */ justify-content: end; /* 靠 writing-mode 的結束端 */ justify-content: left; /* 靠左 */ justify-content: right; /* 靠右 */ justify-content: space-evenly; /* 空白平均分配 (註八)*/
註八: justify-content: space-evenly 是個例外, 主要的 browser 都有支援 (IE11 除外). 說到 IE, Microsoft 已經放棄原本 Edge 的 EdgeHTML (Trident) 引擎了, 新版的 Microsoft Edge 是改用和 Google Chrome 相同的 Chromium 引擎. 而且最重要的是: 它有支援 Win7, 不再像舊版的 Edge 那樣只有 Win10 的版本可以用. 所以如果你還需要 IE11, 建議你改用新版的 Microsoft Edge 試試看. 不過新的 Microsoft Edge 一樣是不支援 ActiveX 的.
- 交錯軸對齊: 同一個主軸內各內容項在交錯軸向上的對齊方式. (預設值為
flex-startstretch)align-items: flex-start; /* 靠交錯軸啟始端 */ align-items: flex-end; /* 靠交錯軸結束端 */ align-items: center; /* 交錯軸居中 */ align-items: stretch; /* 交錯軸向上拉伸至最大值 */ align-items: baseline; /* 交錯軸以文字基準線對齊 */
下列五張圖是 flex-direction: row 搭配 align-items 五種不同的設定值的排版結果. 其他組合也是麻煩各位自行腦補, 這裡就不貼了.
align-items: flex-start
align-items: flex-end
align-items: center
align-items: stretch
align-items: baseline
- 交錯軸排版對齊 (空白控制): 類似 justify-content, 設定如何處理交錯軸上可能多出來的空白.
這個設定項用於內容項太多有折返時, 只有一列 (或一行) 時本項設定忽略. (預設值為flex-startstretch)align-content: flex-start; /* 靠啟始端 */ align-content: flex-end; /* 靠結束端 */ align-content: center; /* 居中 */ align-content: space-between; /* 平均分配, 二側不留空白 */ align-content: space-around; /* 平均分配 */ align-content: stretch; /* 元件拉伸至最大值 */
下列六張圖是 flex-flow: row wrap 搭配 align-content 六種不同的選項的排版結果. 其他組合也是麻煩各位自行腦補, 這裡就不貼了.
align-content: flex-start
align-content: flex-end
align-content: center
align-content: space-between
align-content: space-around
align-content: stretch
記憶小幫手: justify-content, align-items, align-content 這三個名字都好像, 很容易弄糊塗. 各位只要記住有 content 的是設定內容項之間的空白; 而有 align 的是設定交錯軸的. 這樣就比較容易記住了.
Items 設定項
- 伸縮調整: flex 這個設定項是合併接下來要說明的 flex-grow, flex-shrink 和 flex-basis 等三項設定的組合(註九). (預設值為 flex: 0 1 auto)
flex: <'flex-grow'> <'flex-shrink'> <'flex-basis'>
註九: <flex-grow> <flex-shrink> <flex-basis> 三者的組合規矩多了一些. 由於 <flex-grow> <flex-shrink> 二者都是數值, 所以順序上一定是先 <flex-grow> 再 <flex-shrink>, 搭配省略一共只會有三種組合:
- 沒有設定值: 二個都省略.
- 只有一個設定值: 省略後面的 <flex-shrink> 設定值.
- 有二個設定值: 在前的是 <flex-grow> 設定值, 第二個才是 <flex-shrink> 設定值.
不會有省略 <flex-grow> 設定值, 確是要設定 <flex-shrink> 設定值的狀況.
<flex-basis> 則可以任意擺放或省略; 如果在 flex 設定中省略 flex-basis, 它會被設為 0, 而不是它原本的預設值 auto. 這會使這個內容項的寬度在相關的伸展或收縮計算中被當作 0. 請大家小心.
- 伸展權重: 主軸上內容項排列之後如果還有剩餘空白 (空間), 此設定值是內容項延伸佔據此一空白 (空間) 的權重. (預設值為 0, 意即: 不延伸)
flex-grow: 0; /* 權重 0, 不延伸 */ flex-grow: 1; /* 權重值為 1, >0 會延伸 */
簡單應用版
伸展權重最簡單的應用是把想要具有伸展能力的內容項設定成flex-grow:1. 那麼多出來的空白 (空間) 就會均分給flex-grow:1的這些內容項.
精確計算: 如何依權重分配剩餘的空白
flex-grow實際上要比上面說的複雜許多. 實際上, 只要是≥0 的數值都是可以接受的flex-grow設定值. 而分配比例的計算上也分成二大類:
- 權重和 > 1
- 權重和 ≤ 1
權重和 > 1 時, 主軸上的多餘空間是依伸展權重的比例來分配給各內容項.
下面我們經由實例來說明, 例如: flex 容器寬度 500px, 有三個內容項:
- 寬度 100px, flex-grow: 3
- 寬度 120px, flex-grow: 0
- 寬度 160px, flex-grow: 1
首先計算空間的差額 = 500px - (100px+120px+160px) = 120px.
而它將以 3:0:1 的比例, 分配給三個內容項.
三個內容項的權重和 = 3+0+1= 4, 其分配比例分別是:- 3 / 4 = 75%
- 0 / 4 = 0%
- 1 / 4 = 25%
三個內容項的最後寬度分別為 190px, 120px, 190px.
- 100px + (120px × 75%) = 190px
- 120px + (120px × 0%) = 120px
- 160px + (120px × 25%) = 190px
記得我上面說:如果在 flex 設定中省略 flex-basis, 它會被設為 0 嗎? 所以, 我們計算時用的寬度數值要依 flex-basis 的設定值而定, 而不是原本單純的 width (或者 height) 設定.
上面的例子中, 假如, 設定第三個內容項時用的是 flex: 1 (相當於 flex: 1 0 0px), 那麼計算結果就會完全不同.
首先計算空間的差額 = 500px - (100px+120px+0px) = 280px.
三個內容項的最後寬度分別為 310px, 120px, 70px.
- 100px + (280px × 75%) = 310px
- 120px + (280px × 0%) = 120px
- 0px + (280px × 25%) = 70px
接著我們看當權重和 ≤1 時的狀況, 上面計算分配比例的式子裡, 權重和必需換成 1. 也就是說直接拿設定的權重來當分配比例. 結果當然就是多餘的空白 (空間) 不會被完全分配掉.
例如: 把上例的三個內容項的 flex-grow 換成 0.3, 0.2, 0.1,
內容項所得到的多餘空間分別是:- 120px × 0.3 = 36px
- 120px × 0.2 = 24px
- 120px × 0.1 = 12px
在 上試試看
- 收縮權重: 主軸上內容項排列之後如果空間不足, 此設定值是內容項據以收縮以補足空間的權重. (預設值為 1)
flex-shrink: 1; /* 權重值為 1, >0 會收縮 */ flex-shrink: 0; /* 權重 0, 不收縮 */
這裡要注意的是: 收縮和伸展用的不是同一套計算公式. 主軸上排版空間不足時, 各內容項是依寬度加權的比例分攤此一不足空間. 寬度加權比例的算法是:
- 分子為該內容項寬度與收縮權重的乘積.
- 分母則為所有的內容項寬度與收縮權重的乘積和.
這樣子在預設的狀況下, 寬度 (或高度) 較大的內容項就會多收縮一些, 寬度 (或高度) 較小的內容項就會少收縮一些.
簡單應用版
收縮權重最簡單的應用是把不想要具有收縮能力的內容項設定成flex-shrink:0. 那麼不足的空間就會由其他內容項依照他們的大小比例分攤.
精確計算: 如何分攤不足的空間
flex-shrink 和 flex-grow 一樣, 只要是≥0 的數值都是可以接受的flex-shrink設定值. 也一樣分成二大類:
- 權重和 > 1
- 權重和 ≤ 1
但計算公式比 flex-grow 要再複雜一點點.
我們同樣用實例來說明, 例如: flex 容器寬度 500px, 有三個內容項:
- 寬度 100px, flex-shrink: 3
- 寬度 200px, flex-shrink: 2
- 寬度 300px, flex-shrink: 1
一樣, 先計算空間的差額 = 500px - (100px+200px+300px) = -100px.
也就是不足 100px.
寬度加權分配比例的分母 = 3 × 100 + 2 × 200 + 1 × 300 = 1000
所以寬度加權分配比例分別是:- 3 × 100 / 1000 = 30%
- 2 × 200 / 1000 = 40%
- 1 × 300 / 1000 = 30%
三個內容項的最後寬度分別為 70px, 160px, 270px.
- 100px + (-100px × 30%) = 70px
- 200px + (-100px × 40%) = 160px
- 300px + (-100px × 30%) = 270px
同樣的當權重和 ≤1 時, 計算方式也是不同的.
上面計算寬度加權分配比例的式子裡, 需要再乘上權重和.
也就是不會 100% 分攤不足的總量.同樣的把上例的 flex-shrink 換成 0.3, 0.2, 0.1,
內容項所需分攤的空間大小分別是:- -100 px × 30% × 0.6 = -18px
- -100 px × 40% × 0.6 = -24px
- -100 px × 30% × 0.6 = -18px
在 上試試看
有關伸展和收縮權重
我後來覺得應該把這二個權重看成比例: 如果比例和是 ≤ 100% 那就以大家原本宣告的比例來進行分配; 但如果比例和是 > 100% 那就以比例和為 100% 重新計算大家應該分配多少.
- 伸縮基準值: flex-basis 設定內容項在主軸在的初始大小, 所有伸展及收縮的計算都是以此為基準. (預設值為 auto)
/* 指定大小 */ flex-basis: auto; /* 依內容項的設定 */ flex-basis: 25%; /* 容器大小的百分比 */ flex-basis: 10em; flex-basis: 30px; /* 其他指定 HTML tag 大小的語法均可 */
flex-basis要比 width (主軸為水平軸) 或 height (主軸為垂直軸) 的優先權來得高. 沒有設定 flex-basis (或者設為 flex-basis: auto) 則使用 width (主軸為水平軸) 或 height (主軸為垂直軸) 為初始值.
另一個要注意的是flex-basis指定的是 box-sizing 的大小, 所以如果沒有指定 box-sizing:border-box (預設是 box-sizing:content-box) 的話, 還是會像平時使用display:block那樣, 內容項的 border 和 padding 的大小會使內容項超出容器的大小而引起 overflow 的問題.
Part 2:
還在 2020 草案中的新設定項, 目前大部份 browser 都還未支援.所有主流 browser 均已支援./* 依據 內容項 的內容自動調整 */ flex-basis: content; /* 固有的尺寸關鍵詞 */ flex-basis: fill; flex-basis: max-content; flex-basis: min-content; flex-basis: fit-content;
- 交錯軸對齊調整: 調整內容項的交錯軸對齊設定. 意即: 覆蓋掉容器的 align-items 設定. (預設值為 auto)
align-self: auto; /* 使用容器的設定 */ align-self: flex-start; /* 靠交錯軸啟始端 */ align-self: flex-end; /* 靠交錯軸結束端 */ align-self: center; /* 交錯軸居中 */ align-self: stretch; /* 交錯軸向上拉伸至最大值 */ align-self: baseline; /* 交錯軸以文字基準線對齊 */
Part 2:
還在 2020 草案中的新設定項, 目前大部份 browser 都還未支援.所有主流 browser 均已支援.align-self: normal; align-self: start; align-self: end; align-self: self-start; align-self: self-end; align-self: first baseline; align-self: last baseline; align-self: safe <other-'align-self'-value>; align-self: unsafe <other-'align-self'-value>;
- 排列順序調整: 調整內容項在容器內的排列順序(註十). 數值必需是整數, 可以是負數. (預設值為 0)
order: <order_number>
數字小的排前面, 相同大小時依 HTML 本文出現次序.
註十: order 並非 flex 專用設定項.
flex 設定項總整理
flex 容器設定項:
.container {
display: flex | inline-flex;
flex-flow: <'flex-direction'> || <'flex-wrap'>;
flex-direction: row | row-reverse | column | column-reverse;
flex-wrap: nowrap | wrap | wrap-reverse;
justify-content: flex-start | flex-end | center | space-between | space-around;
align-items: flex-start | flex-end | center | stretch | baseline;
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
flex 容器設定項:
.item {
flex: <'flex-grow'> <'flex-thrink'>? || <'flex-basis'>;
flex-grow: <'number'>;
flex-shrink: <'number'>;
flex-basis: auto | <'width'>;
align-self: auto | flex-start | flex-end | center | space-between | space-around;
order: <'integer'>;
}
留言列表