前言


最近經常用到 CSS flex, 但又常常看完把問題解決了, 過幾天要用時又忘了. 雖然網路上也有其他人整理的, 但總是這篇少這個, 那篇又有些設定沒說清楚, 最慘的是有些還弄錯了, 最後只好自己花時間整理.

目前相容性較高的 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 直的排, 只要二行.

.flex-col {
  display: flex;
  flex-flow: column;
}

其他的, 依著需要再慢慢查, 慢慢調整.

flex 相關概念


  1. flex 是應用於區塊式內容的排版 (佈局) 方式. flex 的設定可以分為二大部份:
    • 套用於 container (容器, 外部區塊) 的設定項, 和
    • 套用於 items (內容項, 內部區塊) 的設定項.

    注意: 二者之間並沒有互斥關係, 可以同時運用在同一個 HTML tag 上. container 設定項是當這個 HTML tag 作為容器時用的; 而 items 設定項是當這個 HTML tag 作為上層 flex 容器內容項時用的. 在需要多層套疊 flex 排版時一般都會遇到這種狀況.

  2. flex 主要的設定項都在 container 區塊上, 大部份狀況下只要設定 container 即可. 而用在 items 的設定項大部份狀況是為了個別調整 item 的佈放條件.
  3. flex-Concepts.png

    flex-direction: row 的主軸、交錯軸及他們的啟始端結束端

  4. 一個 flex container區塊內有二個軸向: Main Axis (主軸) 和 Cross Axis (交錯軸). 二個軸向各自有一個啟始端 (flex-start)和結束端 (flex-end). 指定 flex 主軸方向 (flex-direction) 時, 即為指定主軸為 '水平' 或 '垂直' 及它的啟始點結束端位置, 同時也指定另一軸為交錯軸(註一):
    • row: 以水平軸為主軸, 交錯軸為垂直 (由上至下)(註二)
      主軸的啟始端 (flex-start) 在, 結束端 (flex-end) 在
    • row-reverse: 以水平軸為主軸, 交錯軸為垂直 (由上至下)
      主軸的啟始端 (flex-start) 在, 結束端 (flex-end) 在
    • column: 以垂直軸為主軸, 交錯軸為水平 (由左至右)(註二)
      主軸的啟始端 (flex-start) 在, 結束端 (flex-end) 在
    • column-reverse: 以垂直軸為主軸, 交錯軸為水平 (由左至右)
      主軸的啟始端 (flex-start) 在, 結束端 (flex-end) 在

註一: 主軸並不等於水平軸, flex-direction: row 才是把主軸指定為水平軸. 部份網站的圖片及說明搞錯了 (包括部份 google search 排名在很前面的繁中部落格), 請小心.

註二: 想要指定交錯軸為相反方向? 請使用flex-wrap:wrap-reverse.

flex 可以非常方便的應用在 RWD 自適應網頁設計上, 不必費太多心思在調整不同的設備的展現效果, 因此很受歡迎. 以常見的橫排效果來說, 一般可以先固定內容項的最小寛度, 而容器的寬度則依照 browser 或者設備的寬度改變 (直向或橫向). 於是當容器的寬度放不下內容項時, 便自動折返, 然後剩下的橫向空白平均分給這一排當中的各個內容項.

Container 設定項


  1. 啟動開關: 使用 flex 排版的必要設定.
    display設為 flexinline-flex 時啟動 flex 排版功能; 不是這二個關鍵字就會關閉 flex 排版功能, 並忽略相關的 flex 設定項 (包含 container 和它的下層 items).
  2.   display: flex;
      /* 或者 */
      display: inline-flex;
    
  3. 主軸流向與折返: flex-flow 是一個合併設定項, 它合併接下來要說明的 flex-directionflex-wrap 等二項設定.
  4.   flex-flow: <'flex-direction'> <'flex-wrap'>    /* (註三) */
    

    註三: <'flex-direction'> 和 <'flex-wrap'> 沒有要求先後次序. 使用預設值的部份也可以不寫. 二個都想省略時則應該是不用設定 flex-flow 本身.

  5. 主軸流向: 設定 flex 容器內部內容項排版的流動方向. (預設值為 row)
  6.   flex-direction: row;            /* 由左至右, 水平方向排列 */
      flex-direction: row-reverse;    /* 由右至左, 水平方向排列 */
      flex-direction: column;         /* 由上至下, 垂直方向排列 */
      flex-direction: column-reverse; /* 由下至上, 垂直方向排列 */
    

    下列 4 張圖片所展示的是 4 種 flex-driection 選項, 搭配 flex-wrap: wrap 的排版結果.

    dir-row.png

    flex-direction: row

    dir-row-rev.png

    flex-direction: row-reverse

    dir-col.png

    flex-direction: column

    dir-col-rev.png

    flex-direction: column-reverse

  7. 主軸折返: 內容項太多, 容器主軸一列 (或者一行) 放不下時, 是否要折返. (預設值為 nowrap)
  8.   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-directionflex-wrap 的 12 種組合:

    • flex-direction: rowflex-wrap 的組合如下列三圖. 請注意 wrapwrap-reverse 這二張圖的佈放位置為上下鏡射.
    • row-nowrap.png

      flex-flow: row nowrap

      row-wrap.png

      flex-flow: row wrap

      row-wrap-rev.png

      flex-flow: row wrap-reverse

    • flex-direction: row-reverseflex-wrap 組合的佈放位置剛好是上列三張圖的左右鏡射.
    • row-rev-nowrap.png

      flex-flow: row-reverse nowrap

      row-rev-wrap.png

      flex-flow: row-reverse wrap

      row-rev-wrap-rev.png

      flex-flow: row-reverse wrap-reverse

    • flex-direction: columnflex-wrap 的組合如下列三圖. 請注意 wrapwrap-reverse 這二張圖的佈放位置為左右鏡射.
    • col-nowrap.png

      flex-flow: column nowrap

      col-wrap.png

      flex-flow: column wrap

      col-wrap-rev.png

      flex-flow: column wrap-reverse

    • flex-direction: column-reverseflex-wrap 組合的佈放位置剛好是上列三張圖的上下鏡射.
    • col-rev-nowrap.png

      flex-flow: column-reverse nowrap

      col-rev-wrap.png

      flex-flow: column-reverse wrap

      col-rev-wrap-rev.png

      flex-flow: column-reverse wrap-reverse

  9. 主軸排版對齊 (空白控制): 內容項在主軸方向上排列完之後, 如何處理可能多出來的空白(註六). (預設值為flex-start)
  10.   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 五種不同的設定值的排版結果. 由於組合太多了, 其他的麻煩各位自行腦補, 這裡就不貼了.

    just-flex-start.png

    justify-content: flex-start

    just-flex-end.png

    justify-content: flex-end

    just-center.png

    justify-content: center

    just-sp-between.png

    justify-content: space-between

    just-sp-around.png

    justify-content: space-around

    Part 2: 還在 2020 草案中的新設定項, 目前大部份 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 的.

  11. 交錯軸對齊: 同一個主軸內各內容項在交錯軸向上的對齊方式. (預設值為flex-startstretch)
  12.   align-items: flex-start /* 靠交錯軸啟始端 */
      align-items: flex-end   /* 靠交錯軸結束端 */
      align-items: center     /* 交錯軸居中 */
      align-items: stretch    /* 交錯軸向上拉伸至最大值 */
      align-items: baseline   /* 交錯軸以文字基準線對齊 */
    

    下列五張圖是 flex-direction: row 搭配 align-items 五種不同的設定值的排版結果. 其他組合也是麻煩各位自行腦補, 這裡就不貼了.

    aitem-flex-start.png

    align-items: flex-start

    aitem-flex-end.png

    align-items: flex-end

    aitem-center.png

    align-items: center

    aitem-stretch.png

    align-items: stretch

    aitem-baseline.png

    align-items: baseline

  13. 交錯軸排版對齊 (空白控制): 類似 justify-content, 設定如何處理交錯軸上可能多出來的空白.
    這個設定項用於內容項太多有折返時, 只有一列 (或一行) 時本項設定忽略. (預設值為flex-startstretch)
  14.   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 六種不同的選項的排版結果. 其他組合也是麻煩各位自行腦補, 這裡就不貼了.

    acont-flex-start.png

    align-content: flex-start

    acont-flex-end.png

    align-content: flex-end

    acont-flex-center.png

    align-content: center

    acont-flex-sp-between.png

    align-content: space-between

    acont-flex-sp-around.png

    align-content: space-around

    acont-flex-stretch.png

    align-content: stretch

記憶小幫手: justify-content, align-items, align-content 這三個名字都好像, 很容易弄糊塗. 各位只要記住有 content 的是設定內容項之間的空白; 而有 align 的是設定交錯軸的. 這樣就比較容易記住了.

Items 設定項


  1. 伸縮調整: flex 這個設定項是合併接下來要說明的 flex-grow, flex-shrinkflex-basis 等三項設定的組合(註九). (預設值為 flex: 0 1 auto)
  2.   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. 請大家小心.

  3. 伸展權重: 主軸上內容項排列之後如果還有剩餘空白 (空間), 此設定值是內容項延伸佔據此一空白 (空間) 的權重. (預設值為 0, 意即: 不延伸)
  4.   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

    上試試看

  5. 收縮權重: 主軸上內容項排列之後如果空間不足, 此設定值是內容項據以收縮以補足空間的權重. (預設值為 1)
  6.   flex-shrink: 1    /* 權重值為 1, >0 會收縮 */
      flex-shrink: 0    /* 權重 0, 不收縮 */
    

    這裡要注意的是: 收縮伸展用的不是同一套計算公式. 主軸上排版空間不足時, 各內容項是依寬度加權的比例分攤此一不足空間. 寬度加權比例的算法是:

    • 分子為該內容項寬度收縮權重乘積.
    • 分母則為所有的內容項寬度收縮權重乘積和.

    這樣子在預設的狀況下, 寬度 (或高度) 較大的內容項就會多收縮一些, 寬度 (或高度) 較小的內容項就會少收縮一些.

    簡單應用版

    收縮權重最簡單的應用是把不想要具有收縮能力的內容項設定成flex-shrink:0. 那麼不足的空間就會由其他內容項依照他們的大小比例分攤.

    精確計算: 如何分攤不足的空間

    flex-shrinkflex-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% 重新計算大家應該分配多少.

  7. 伸縮基準值: flex-basis 設定內容項在主軸在的初始大小, 所有伸展及收縮的計算都是以此為基準. (預設值為 auto)
  8.   /* 指定大小 */
      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那樣, 內容項borderpadding 的大小會使內容項超出容器的大小而引起 overflow 的問題.

    Part 2: 還在 2020 草案中的新設定項, 目前大部份 browser 都還未支援.

      /* 依據 內容項 的內容自動調整 */
      flex-basis: content;
      /* 固有的尺寸關鍵詞 */
      flex-basis: fill;
      flex-basis: max-content;
      flex-basis: min-content;
      flex-basis: fit-content;
    
  9. 交錯軸對齊調整: 調整內容項交錯軸對齊設定. 意即: 覆蓋掉容器align-items 設定. (預設值為 auto)
  10.   align-self: auto /* 使用容器的設定 */
      align-self: flex-start /* 靠交錯軸啟始端 */
      align-self: flex-end   /* 靠交錯軸結束端 */
      align-self: center     /* 交錯軸居中 */
      align-self: stretch    /* 交錯軸向上拉伸至最大值 */
      align-self: baseline   /* 交錯軸以文字基準線對齊 */
    

    Part 2: 還在 2020 草案中的新設定項, 目前大部份 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 center
      align-self: unsafe center
    
  11. 排列順序調整: 調整內容項容器內的排列順序(註十). 數值必需是整數, 可以是負數. (預設值為 0)
  12.   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'>;
}

flex 應用篇


CSS: 如何使用 Flex 完成 table 效果

參考連結


  • CSS Flexible Box Layout Module Level 1
    W3C Candidate Recommendation, 19 November 2018
  • CSS Flexible Box Layout Module Level 1
    Editor’s Draft, 10 March 2020
  • Can I Use 網站 flex 部份
  • 圖解:CSS Flex 屬性一點也不難| 卡斯伯Blog - 前端,沒有極限
  • A Complete Guide to Flexbox
  • 详解 flex-grow 与 flex-shrink
  • 深入理解 flex 布局以及计算
  • flexbugs

    MagicJackTing 發表在 痞客邦 留言(1) 人氣()