[CSS] Flex/Grid Layout Modules, part 1

万事起头难,只要不起头,就一点都不难了。

在这个充满着 CSS Frameworks 的年代,人人有功练,人人有版切,身为老屁股的我只能在沙滩上晒乾,而且还卖不到钱。

铁人赛就当作自己还有在喘气的证明。


前言

其实写 CSS 框架应该比较多观众,但就一个码农来说,自己动手做会比较有趣一点。而且那些框架实在有点五花八门,就是你去 Google 一些关键字,诸如「超好用」「XX 天就上手」「20XX 热门框架」然後就会跑出一堆。

我没有阻止你用,只要能赚钱的框架就是好框架。

君不见 jQuery 的 $ 还是屹立不摇。

不过 jQuery 有点离题了就是。


目前 w3c 对於 Flexbox, Grid 这两件事情算是有认真在更新。虽然说 Grid 已经 CR 不过又挂上 Draft,该吵的东西也还没有结论(例如 subgrid 会推迟到 Level 2 才可能会推出 #Issue958)。

Grid 从 2016 年 CR 至今也 5 个年头了,基本上在 Caniuse 上面也呈现出不错的支援度。热门的 TailwindCSS 基本上也有采用 Grid 来做排版,至於怎麽用不要来问我,我没有很熟(哈)。

Flexbox 就不用再多说,我在 2012 年看他 CR 後,又到了 2016 才慢慢被推广出来,算起来已经 9 年了。至於说为什麽那麽不红(或是不普及?),可能要去问一下前阵子才入土的 IE 吧。

至於所搭配的 Media Query 会在系列中提及,会顺便讲一点,不会着墨太多。


目录

  1. Flexbox 基本介绍
  2. Flexbox 能与不能
  3. Flexbox 与 Media Query
  4. Flexbox 其他相关资讯
  5. Grid 基本介绍
  6. Grid 能与不能
  7. Grid 与 Media Query
  8. Grid 演算机制
  9. Grid 与 Flexbox 比较
  10. Grid / Flexbox 混合应用
  11. Grid 进阶与新功能
  12. Grid 在浏览器上的各种差异
  13. 那些跟 Grid 相关的 CSS 样式设定
  14. 那些关於 Level 2 的事情

以上这些,其实就把超长篇幅的文章然後分割成 30 份这样吧(哈)。


其实我不知道 Flexbox 还有谁想看?

这年头好像用框架比较快,谁理你背後的原理是什麽呢?


Flexbox

我们先来看看可使用状况。基本上除了 IE 以外,是不用担心使用上的问题。

https://ithelp.ithome.com.tw/upload/images/20210906/20001433IXK91r6iZh.png

Flexbox 的基本盘是对应从 CSS2.1 以来的四种编排模式,

  1. 区块(block
  2. 行内(inline, inline block
  3. 表格资料(table, 或各种资料集设计)
  4. 定位区块(使用 position 的各种变化)

虽然是这样讲,不过这就跟当年 CSS1/2/2.1 开始流行後,从 <table> 慢慢转向 <div> 後,接着到了 HTML5 开始讲语意化。出了一些很奇妙的事情,

  1. <table> 退流行了所以我都用 <div> 很潮~
  2. <div> 就用 position 神马都可以排,超 DUE 的~
  3. float 简直凌波微步,我得意的飘~
  4. HTML5 说要语意化,用 <div> 简直罪恶!

其他的就不提了,我只想说,为了语意化而语意化真的挺恶心的。


基本介绍

Flexbox 区块元件主要构成如下,

  1. Flex 容器(Container
  2. Flex 元件(Items
  3. 容器尺寸(通常我们叫做 宽度
  4. 交叉轴尺寸(通常我们叫做 高度
  5. 容器主轴/交叉轴(依据排列方式决定哪一个方向为主轴)

https://ithelp.ithome.com.tw/upload/images/20210906/200014335PMJSlRi4i.png

如果你把 Flex 容器主轴方向设定为 column 的时候,上图的主要轴、交叉轴就会交换。


Flex 容器

设定容器我们可以使用 display 来达成,容器可以设定成两种,

  1. display: flex 亦即使用区块(Box Level)来设定容器。
  2. display: inline-flex 亦即使用行内(Inline Level)来设定容器。

无轮你设定的是哪一种,需要留意 Flex 容器会忽略以下特性,

  1. float, clear 会被忽略。
  2. vertical-align 无法应用在 Flex 元件上。
  3. ::first-line, ::first-letter 这两个拟似元件无法套用至容器上。

倘若元素指定 display: inline-flex 的话,在计算样式值(Computed Value)会呈现 flex 而不是 inline-flex,这一点请留意。


样式设定

在容器当中,以下列举常用的样式设定,

样式 预设值
flex-direction row, row-reverse, column, column-reverse row
flex-wrap nowrap, wrap, wrap-reverse nowrap
flex-flow <flex-direction> <flex-wrap> 无预设值

容器主要轴、交叉轴会因为方向性的不同而交换,其下属性所适用的方向也会不同。

另,轴方向也会因为 writing-mode 的影响而有不一样的呈现,关於 Write Mode 可以参考 w3c 上面的说明(CSS Writing Modes Level 4)。

容器中对於影响元件的属性有这些,

样式 预设值
justify-content flex-start, flex-end, center, space-between, space-around flex-start
align-items flex-start, flex-end, center, baseline, stretch stretch
align-content flex-start, flex-end, center, space-between, space-around, stretch stretch

需要留意的是 align-content 只能适用在多行的 Flex 容器当中,在预设的容器设定中使属於单行 Flex 容器,套用这个样式是无效的设定。


轴方向

容器些样式设定会因为轴方向性的不同而应用在不同的方向上(会跟着轴转动)。请留意一个点,轴方向并不总是内容流向,我所举的例子都是以惯用方向为主(由左至右,由上到下)。

举个例子来说,

https://ithelp.ithome.com.tw/upload/images/20210906/20001433rwo2U4VUpP.png

另外关於 align-content 则是在多行容器中才会套用到这个样式。所谓的 多行 的意思即是 wrap 的情况下,产生超过一行的 Flex 元件时,该样式就会被套用。

请留意 column 这个方向在没有容器尺寸的情况下,无法产生 wrap 的效果,亦即并无 多行 的情况存在,且关於 wrap 的相关样式也会失效。

举一个套用 wrap 产生多行 Flex 容器的例子,

https://ithelp.ithome.com.tw/upload/images/20210906/2000143320p6fegr8X.png

跟其他两个样式相同,当轴方向转动时,align-content 也会跟着轴转动,亦即应用的方向会不同。


Flex 元件

任何被 Flex 容器所包含的第一层子元件,都会被转成 Flex 元件,而自身属性会转换成 Flex 格式内容(Flex formatting context),可以当作一般区块元件看待,但实际上并不是(Like Box Model, bot NOT.)。如同上述所说,这些元件并无法使用 float 尔等设定,这一点需特别留意。另外,假设这些元件被定义了静态定位(positionstatic, absolute)时,该元件就不属於 Flex 容器预设的主轴流向,会跳脱任何关於 Flex 容器所带来的影响。

元件自身也可以使用 display: flex 来将自己转变成容器,这样的作法在需要复杂结构时,可以自由变化使用。


样式设定

Flex 元件有以下样式可以使用,

样式 预设值
flex-grow 数字(负值无效) 0
flex-shrink 数字(负值无效) 0
flex-basis auto, content宽度 auto
flex none<flex-grow> <flex-shrink> <flex-basis> 0 1 auto
order 数字(可为负值) 0
align-self auto, flex-start, flex-end, center, baseline, stretch auto

其中 flex 除了 none 外,还有几个关键字可以使用,

关键字 等值
none 0 0 auto
initial 0 1 auto
auto 1 1 auto
<正整数> <正整数> 1 0

flex, flex-grow, flex-shrink, flex-basis

在绝大多数的 CSS 框架中,我们比较常见的设定大多都是这些组合,

flex: 0 1 auto;
// 或
flex: 1 0 100%;
// 或
flex: 1 0 50%;
max-width: 50%;

然後就没有然後了。因为人家这样用所以你就跟着这样用,至於为什麽要这样用好像也不是挺重要的事情。

没关系,我们就来看看到底发生了什麽事情?

属性 说明
flex-grow 将元件依照此设定数字的权重来填满容器的剩余空间。
flex-shrink 当容器剩余空间不足时,依照此权重来压缩元件。
flex-basis 在水平(row)排列容器中,等同於 width 样式,在同时设定时将会忽略 width 的样式设定。但若为 autocontent 时,则 width 样式将会覆盖。而当你设定为 0 时,结果会跟 content 雷同,但权重不同。

flex-grow 计算方式,可以依照此公式来计算,

剩余空间 x <flex-grow> / sum(<flex-grow>)

剩余空间怎麽来的呢?

容器空间 - sum(<flex-basis>||<width>)

但是,

当你的 flex-grow 总和小於 1 的时候,事情就不是这样了。在所有 Flex 元件的 flex-grow 总和小於 1 时,该总和会直接当作 1 来使用。换句话说,上面的公式会变成,

剩余空间 x <flex-grow> / 1

这样计算下来,容器就可能会有剩余空间没有分配的情况。

另外,当你的 flex-grow 有搭配 max-width 使用时,所计算出来的元件若大於 max-width,则会优先取用 max-width 的设定值来使用,计算出来的填满尺寸将会被忽略。这也是造成容器没有被填满的另外一个原因。

https://ithelp.ithome.com.tw/upload/images/20210906/20001433sr8P3Le18a.png

flex-shrink 计算方式,可以依照此公式来计算,

溢出空间 x <flex-shrink> * <width>) / sum(<flex-shrink> * <width>)

溢出空间的计算方式为(注意,这为负值),

容器空间 - sum(<flex-basis>||<width>)

flex-grow 雷同,当你的 flex-shrink 总和小於 1 的时候,他并不会拿所有的溢出空间来计算,你的真实的溢出空间要先经过这样的计算,

溢出空间 x sum(<flex-shrink>) / 1

算出新的溢出空间之後,才回头套用上面的公式去计算。

https://ithelp.ithome.com.tw/upload/images/20210906/20001433bn66UeWPUg.png


flex-basis 的魔术

特别把这件事情拿出来讲的原因,是因为他跟 width 实在有说不清的爱恨纠葛。如同上述提及了一些优先权的事情,这边给一个比较清楚的比较表,

设定 宽度有效值
flex-basis: 50px; 50px
flex-basis: 50px; width: auto; 50px
flex-basis: content; width: 60px; 60px
flex-basis: 70px; width: 60px; 70px
flex-basis: auto; width: 80px; 80px
flex-basis: 90%; width: 80px; 90%
flex-basis: 90%; max-width: 80%; 80%

基本上,只要 flex-basis 不是使用关键字 auto, content 的情况下,优先权一律覆盖 width 的设定。但是,这个数值设定会受到 max-width 的影响而有所不同。

https://ithelp.ithome.com.tw/upload/images/20210906/20001433C0rk1wy4QA.png


align-self

这个 Flex 元件属性有一个 必要条件,倘若以 row 为主轴方向,你的容器必须要有交叉轴的尺寸设定,不然这个属性是无法有效呈现的。

https://ithelp.ithome.com.tw/upload/images/20210906/20001433Y5i13Xb7b4.png


order

故名思义就是 Flex 元件顺序的设定,但请留意,如果你的元件使用了静态定位设定(positionstatic, absolute)时,因为元件会跳脱 Flex 主轴流向,所以此时的顺序设定会失效。

Flex 元件会有一个隐性的定义,在 order 样式混用的情况下,没有设定 order 样式的 Flex 元件,会带有一个 order: 0 的效果。所以,若你在多个元件使用 order 时,请务必确认每个元件都有设定你想要的 order 样式。

举例来说,

<div class="flex">
    <div class="item item-1"></div>
    <div class="item item-2"></div>
    <div class="item item-3"></div>
</div>
.flex {
    display: flex;
    flex-flow: row;
}

.item {
    flex: 1 0 33.333333%;
    max-width: 33.333333%;
}

.item-1 {
    order: 1;
}

.item-2 {
    order: 3;
}

.item-3 {
    order: 2;
}

最终呈现的结果如图,

https://ithelp.ithome.com.tw/upload/images/20210906/20001433CK1dOwfz89.png

有一点请留意,如果你的轴方向是反向(row-reversecolumn-reverse)的话,顺序的设定也会反过来。


小记

以上这些是 Flexbox 的基本概念介绍,其实讲起来并没有很多艰深的东西,真正比较枯燥乏味的会再後面继续介绍,如果不想理解 Flexbox 的演算机制的可以跳过没关系(笑)。

本篇内容理论上可以应付五成以上的容器设定。为什麽只有五成?因为我还有 留白 的部分没有讲到,那些算一算大概是剩下的四成五左右。

下一篇会着墨於那个四成五,至於剩下的 5% 就放最後吧,反正都是一些很硬的演算机制。


同步放送:

[CSS] Flex/Grid Layout Modules, part 0
[CSS] Flex/Grid Layout Modules, part 1


<<:  【Day06】提升(Hoisting)

>>:  [想试试看JavaScript ] 运算子 (算术运算子)

Day 8 Compose UI Constraint Layout

今年的疫情蛮严重的,希望大家都过得安好,希望疫情快点过去, 能回到一些线下技术聚会的时光~今天要了解...

【Day18】导航元件 - Breadcrumb

元件介绍 Breadcrumb 是一个导航元件,用於显示当前系统层级结构中的路径位置,并且点击路径能...

【Day10】:库函数包装—对於底层暂存器的操纵(上)

什麽是暂存器 register? 暂存器顾名思义就是可以存放资料的地方,那也就是记忆体的一种罗? 记...

理解 HTTP(三):透过 HTTP 上网安全吗?浅谈网路安全、HTTPS、中间人攻击

聊了 HTTP 的基本概念(网站内容是怎麽被下载到电脑里的?、Method、Status Code)...

改善R^2 (2) | ML#Day25

接续上一篇,第二种改善R^2的经验。 2 . 减少极端值的影响 所谓的模型输出的准确性,也可以这麽想...