[CSS] Flex/Grid Layout Modules, part 6

你如果会用 Excel,那 Grid 就应该不陌生。

不过老实讲,我也不太会用 Excel。


Grid Layout Module

其实他已经 CR 好一阵子了,目前多数主流的 CSS 框架已经开始采用。所以并不是你不会用或是没有使用到这个东西,而是你已经在用了,但是你不知道他背後是使用 Grid Layout 而已。

https://ithelp.ithome.com.tw/upload/images/20210910/20001433vFk6Cl6xIf.png

Grid 的基本结构就跟我刚刚提到的 Excel 很像,他是一个矩形区块,包含了栏与列的设定。具体想要解决的问题在於「排版」这件事情,但试着回想这将近 20 年来排版的变化,是不是又很像回到了 <table> 的年代呢?


Grid 基本介绍

Grid Layout 基本上包含了下列结构:

  1. Grid 容器
  2. Grid 元件(或其子集合)
  3. 容器尺寸(宽度与高度)

相较之下,他没有像是 Flexbox 有所谓的 轴方向 的问题。

https://ithelp.ithome.com.tw/upload/images/20210910/20001433TA5oOZveCo.png

基本的 Grid 容器组成就是这样,看起来很简单。由於没有 Flexbox 的主要轴、交叉轴的问题,所以操作起来相对会比 Flexbox 容易一点点。


Grid 容器

首先,宣告一个 Grid 容器一样是使用 display 属性,

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

这里跟 Flexbox 一样,被定义出来的容器属於 grid formatting context,所以原本在 Flexbox 上面会失效的事情,在这边也一样会失效。

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

同样的,倘若元素指定 display: inline-grid 的话,在计算样式值(Computed Value)会呈现 grid 而不是 inline-grid,这边也是跟 Flexbox 一模一样。

最大的不同点在於,Grid Layout 带来了 格线轨道Grid track)的概念。

一个 Grid 容器依据横向(row)与纵向(column)来分割,这个分割的动作就是一个 格线轨道Grid track)。这个轨道又可以区分为 网格单元Grid cell)与 网格格线Grid line)两个面向。然後,网格单元与格线还可以区分出 命名单元(区域)命名格线 的差异。

在这里先有个底就好,後面会大量提及这些事情。


容器尺寸与限制

Grid 容器本身可以设定一定尺寸,但若不指定容器尺寸的情况下,Grid 容器尺寸会依照这些项目的总和来决定外部尺寸,

  1. 每一个格线轨道尺寸
  2. 包含间隔(gap
  3. 每个轨道内容的最大(或最小)的内容尺寸(min-contentmax-content

这样会决定出 Grid 的最小或最大尺寸该是多少。通常在 轨道尺寸 够用的情况下都不会有太大问题。问题在於不够用的情况,倘若真的不够用,就会采用轨道尺寸下的 min-content 来当作轨道尺寸,你的 Grid 容器尺寸设定基本上会无效。我後面会继续举例,这边就先有个概念即可。

另外,倘若装置对於网格有其限制,那麽在网格容器内的网格格线就会被限制。

  1. 当你的网格元件跨度(span)超出网格格线限制,则会被限制在最後一个网格轨道上。
  2. 当你的网格元件完全超出网格格线限制,则会被限制在最後一个网格轨道上,且网格轨道永远为 1。

关於尺寸限制其实不太容易遇到,除非你故意的,或是你真的在一些比较特殊的显示装置要使用的时候,才必须留意这件事情。


网格单元与格线

在 Grid 容器当中,被 grid-template-rowsgrid-template-columns 分割出来的区域,叫做网格单元(Grid cell),而这个容器单元会有所谓的格线,这个格线就是网格格线(Grid line)。

https://ithelp.ithome.com.tw/upload/images/20210910/20001433c9ZUAz9fKM.png

上图用数字标出来的就是网格格线(Grid line),网格格线还有分成两种:

  1. 一般网格格线(或命名网格格线)
  2. 隐性网格格线(implicit grid

关於 Implicit Grid 我後面会介绍,如果你想看官方解释也可以:

The Implicit Grid

但我不保证看得懂。


容器样式

这边与 Flex 不同的点在於,他的样式应用非常多,也相对复杂。

样式 可用值 预设值
grid-template-columns none, <轨迹清单> none
grid-template-rows none, <轨迹清单> none
grid-template-areas none, <字串> none
grid-template 上述三个设定的简写 none
grid-auto-columns <轨迹尺寸> auto
grid-auto-rows <轨迹尺寸> auto
grid-auto-flow row, column, dense row
grid 上述全部的简写 none

以上是一个 Grid 容器可以设定,关於容器本身的样式。接着我们来看看里面的设定值到底写了些什麽东西。


轨迹清单

我们在设定网格的时候,比较常见的作法会类似这样,

.grid-container {
    display: grid;
    grid-template-columns: 100px 500px;
    grid-template-rows: 100px 100px 100px;
}

欧,你说我怎麽不用 fr 来当例子?这个新的单位後面会提到。这样我们就可以拥有一个很简单的网格容器,他包含了,

  1. 两个栏(columns),宽度为 100px500px
  2. 三个行(_row),高度皆为 100px

这边也可以搭 CSS 运算涵式或关键字使用,例如,

.grid-container {
    grid-template-columns: 100px repeat(50px, 3) minmax(min-content, 1vw);
}

另外,前述有提到了网格格线(Grid line),在这边可以替你的格线命名,预设在不命名的情况下,都是以 数字 来定义网格格线,

.grid-container {
    grid-template-row: [first side-begin] 100px [side-ended main-begin] 100px [main-ended last];
}

格线名命名的规则就是用 [] 把他包起来,这样你的格线就会有一个特别的名字。另外请留意,不要用 -start-end 来当作名称,这样的命名方式会踩到网格格线的雷。另外,名字可以一样,但在指定的时候必须要特别指定你要在哪一个格线。

https://ithelp.ithome.com.tw/upload/images/20210910/20001433pHgAgmZdtU.png

当你使用命名格线时,在指定网格单元时就可以使用名称,而不是去计算数字,相对来说会比较方便一些。但如果你把网格格线的名称都设定成同一个名字,那你在取用的时候还是要指定对应的数字才行。

但,都要你取名字了,尽量还是不要一样比较好。

至於 -start-end 的雷,後续提到隐性网格会继续讲。


grid-template-areas 的字串定义

这个样式的使用方式比较特别,他是需要定义每一个 网格单元 的名字,也就是说,我们可以写成类似这样,

.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar main main"
        "sidebar main main";
}

所以,这个时候你的 HTML 就只会有三个元件,

<div class="grid-container">
    <nav class="nav">
    </nav>
    <aside class="sidebar">
    </aside>
    <main class="main">
    </main>
</div>

而这三个元件搭配的设定会是这样,

.nav {
    grid-area: nav;
}

.sidebar {
    grid-area: sidebar;
}

.main {
    grid-area: main;
}

最终你会得到这样的结构,

https://ithelp.ithome.com.tw/upload/images/20210910/20001433FIklpMju77.png

当然,这种写法不是没有限制的,

  1. 命名规则组合必须要是矩形(N x M
  2. 不同区块不能使用重复名称
  3. 可以用 . 来忽略(不使用)该区域
  4. 任何非预期字串都会被视为空白

所以说,以下的写法 皆不合法

/* 以下写法不合法 */
.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar sidebar main"
        "sidebar main main";
}

/* 以下写法不合法 */
.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar . main"
        "sidebar main main";

/* 以下写法不合法 */
.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar main"
        "sidebar main main";
}

另外,除了容器本身限制之外,当你的网格元件使用了 未命名 的网格单元时,会对隐性网格格线产生副作用。这个部分我後面会再次提及。


隐性网格

隐性网格(Implicit Grid)在网格系统中是一个很特别的存在。官方对他的说明简单的解释可以这麽形容,

你的设定不足以画出一个网格,所以我 鸡婆的 帮你把缺的格线补上。

何谓设定不足以画出网格?例如,

  1. 没有设定栏(grid-template-columns)或列(grid-template-rows
  2. 超出设定栏或列的数量
  3. 设定了 grid-area 却不存在於 grid-template-areas 当中

隐性网格有两个样式可以使用,

  1. grid-auto-columns
  2. grid-auto-rows

设定的数值仅接受这些,

  • auto
  • min-content
  • max-content
  • fr
  • minmax()
  • 百分比单位数值(例如 10%

关於隐性网格在这边就不赘述,後面会继续提到这个东西。


网格单元顺序

网格单元的顺序其实跟 Flexbox 使用 order 的方式是一样的,不过,他可以使用 z-index 来定义哪一个格子比较高。

https://ithelp.ithome.com.tw/upload/images/20210910/20001433dqbIWo7Jpt.png

通常会搭配 z-index 大多数是网格容器本身有卷动需求的时候会特别这样做。


对齐与间隔

基本上网格容器的对齐跟 Flexbox 几乎是完全一样。我在这边如果再讲一次好像又太浪费篇幅。基本上以下这些东西跟 Flexbox 完全一样,

  • align-items
  • align-self
  • align-content
  • justify-content

然後网格系统多了这几个,仅能在 Grid 容器下使用,

  • justify-items
  • justify-self
  • place-items
  • place-self
  • place-content

间隔的部分也跟 Flexbox 使用同一套 gap,其实就是共用同一套 w3c 的规范,

CSS Box Alignment Module Level 3

其实这边没有什麽新的东西,除了 Grid 才能使用的对齐方式外,其他的东西都跟 Flexbox 几乎一样。

justify-itemsjustify-self 是在容器系统中,Grid 单元主要轴向维度作对齐的样式设定。与 align-items, align-self 则是交叉向维度做对齐(或填充)的样式。

Grid Layout 并没有指定主要轴与交叉轴,这边的轴向设定是跟着系统的文字流向而决定的。所以他没有像是 Flexbox 可以明确的指定谁是主要轴,谁是交叉轴。

以下稍微说明一下 Grid 多出来的几样东西,

样式 设定值 预设值
justify-items normal, stretch, <baseline-position>, <overflow-position>?, [ <self-position>, left, right ], legacy, legacy && [ left | right | center ] legacy
justify-self auto, normal, stretch, <baseline-position>, <overflow-position>? [ <self-position>, left, right ] auto
place-items <'align-items'> <'justify-items'>? 无特定预设值
place-self <'align-self'> <'justify-self'>? 无特定预设值
place-content <'align-content'> <'justify-content'>? 无特定预设值

基本上可以当作是在 Flexbox 上因为需要多行而无法特别设定的东西,在 Grid 里面可以直接设定这样的样式。当然,如果你的 Grid 单元数量没有到两个栏或列以上,其实是看不出什麽效果的。


小记

今天先这样,Grid 容器基本上东西太多,要一次讲完有点困难。明天会搭配图片来解说各种关於容器的设定,还有一些比较基本的运算方式。


目录与小节:
[CSS] Flex/Grid Layout Modules, part 1


部落格同步放送:
[CSS] Flex/Grid Layout Modules, part 6


<<:  [Day03] swift & kotlin 入门篇!(1) 基础语法-变数与常数宣告

>>:  [FGL] OPEN WINDOW WITH 画面档

追求JS小姊姊系列 Day20 -- 工具力,原来如此:继承。

前情提要: 在上一集解释了工具力的来源 方函式:还记得之前提到的建构式模式吗? 只要提供一个原型,就...

Day 2. 安装Unity

耶,到了第二天了,虽然还不是很懂VR软件要怎麽开发,也没有头盔,总之就先下载Unity再说~   下...

Day 4. 今天要干嘛?

好消息,我找到一个贴文跟一个影片,所以逛头盔这件事可以延後一点做: How to use Unity...

Day 23 摘要就是抓住重点!

千丝万缕就像风一样的吹过,但总要抓住想要的那一丝一缕。 《iT邦帮忙铁人赛的观点》(以下简称铁人赛)...

谁温暖了资安部-28(破口)

本来要搭文湖线,坐到南京复兴站,结果,我到大直站就下车了,走出捷运站後,叫了计程车,回阳光街。 返回...