[CSS] Flex/Grid Layout Modules, part 8

我先问一个问题,如果我有一个 3x3 的 Excel 方块,请问我有几条格线?

Grid 容器中的格线是整个排版定位中的灵魂,但,他没有 Excel 那麽单纯。


网格格线

Grid 容器的灵魂就是网格格线,我们在宣告一个 Grid 容器的时候,每个方块就会产生相对应的格线,而这些格线当中还会有先前提及的隐性格线(implicit grid)的设计。

我们从 Grid 容器先看起,首先我们定义一个 3x3 的 Grid 容器,

.grid-container {
    display: grid;
    grid-template-rows: repeat(3, 1fr);
    grid-template-columns: repeat(3, 1fr);
}

https://ithelp.ithome.com.tw/upload/images/20210912/20001433q51kLvo5ie.png

当然,如果你很明确的定义你的容器,那麽他就会给你很明确的网格格线。如果你没有、或缺少定义网格区块的话,那麽就会落入隐性格线的设定里面。我们这边暂且先不谈,後面会继续提及隐性格线的相关事宜。

首先,既然格线有数字,那麽那些数字代表了什麽意思?

  • 每个 Grid 单元的 开始结束 的地方
  • 依照格线的数字可以定义一个 Grid 单元的尺寸
  • 可以使用负数
  • 可以命名
  • 命名格线请避开 -start-end 结尾

这麽一来,我如果要做一个 Grid 单元像是这样,

.grid-item {
    grid-row-start: 1;
    grid-row-end: 3;
    grid-column-start: 2;
    grid-column-end: 4;
}

我还没介绍网格单元的基础,这边请先当作你知道这件事情(欸)。

https://ithelp.ithome.com.tw/upload/images/20210912/20001433Ity2Oucv0Q.png

接着我们再回来看 Grid 容器设定,由於格线可以命名,所以我们可以这样写,

.grid-container {
    display: grid;
    grid-template-rows: [first] 1fr [second] 1fr [boo] 1fr [last];
    grid-template-columns: repeat(3, [foo] 1fr [boo]);
}

由於我中间混合了 repeat() 的写法,所以他会有一点点不一样,

https://ithelp.ithome.com.tw/upload/images/20210912/20001433TCkYquZkJ8.png

虽然说格线有了名字,但原本的数字还是可以使用的。既然有名字,那麽我们的 Grid 单元就能使用名字来决定要使用的区域,

.grid-item {
    grid-row-start: second;
    grid-row-end: last;
    grid-column-start: foo 2;
    grid-column-end: boo -1;
}

好的,关於 <命名格线> <数字> 的结构请先当作你知道这件事情(灿笑)。

https://ithelp.ithome.com.tw/upload/images/20210912/20001433Z9nOPJ5h4L.png

由於我使用了 repeat() 的关系,所以你会发现同一条格线会有两个名字,在格线的命名上这样是合法的,也就是说一条格线可以有好几种名字,如果你不觉得烦的话可以这样做没关系。

以下是奇怪的例子请不要乱用,

.grid-container {
    display: grid;
    grid-template-rows: [hello world please begin here] 1fr [second] 1fr [boo] 1fr [last];
    grid-template-columns: repeat(3, [foo boo too] 1fr [xoo]);
}

隐性格线 The Implicit Grid

这件事情其实是一种辅助的设计,当你的 Grid 容器设定并没有办法满足一些使用状况的时候,Grid 的渲染引擎会加上这种隐性格线来当作辅助,目的是用以确保整个网格系统的完整性。简单来说,就是当你的网格元件超出了容器设定时,就会产生出隐性网格轨道,然後网格轨道就会带着网格格线出现。

The Implicit Grid

官方的说明再贴一次,虽然不一定看得懂。

至於,哪些状况会产生隐性网格轨道(隐性格线)呢?

合理的说法是,

  • 网格单元超出了网格容器的设计

所谓的 超出 了网格容器的设计,代表的就是在设定上的资料量,超过了原有网格容器可容纳的网格单元数量。就像是 3x3 的方格,你硬要放入 4x4 的资料的意思一样。网格系统为了确保网格轨道的正确性,就会增加轨道来放入这些资料,这些 被增加 的轨道,就是所谓的隐性网格轨道(Implicit Grid Tracks),而这些轨道的产生就会带来了隐性网格格线(Implicit Grid Line)。

但是,

实际上(现实面)的状况是,

  • Grid 容器设定不完整
  • Grid 单元指定了不存在的网格格线(或网格单元)
  • 使用命名 Grid 单元,却不存在於 Grid 容器规范中

虽然说这些隐性格线目的是为了确保 Grid 容器的完整性,但是,我觉得更多的部分应该是要 防止人类随意乱写样式造成的错误。我们可以来看看这些例子,

.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 100px);
    grid-template-rows: repeat(2, 100px);
}
<div class="grid-container">
    <div class="grid-item">1</div>
    <div class="grid-item">2</div>
    <div class="grid-item">3</div>

    <div class="grid-item">4</div>
    <div class="grid-item">5</div>
    <div class="grid-item">6</div>

    <!-- 实际上只设定了两列,但这边是第三列资料 -->
    <div class="grid-item">7</div>
    <div class="grid-item">8</div>
    <div class="grid-item">9</div>
</div>

在没有定义 grid-template-rows 的情况下,所有的列(row)的产生都算在隐性轨道头上。

https://ithelp.ithome.com.tw/upload/images/20210912/20001433GPozJIItnt.png

尔或者是指定了一个奇怪的网格轨道,迳而产生了隐性网格轨道,造成後面网格呈现出现不同的流向,

.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 100px);
    grid-template-rows: repeat(3, 100px);
}

.grid-item:first-child {
    grid-column: 1 / 5;
    grid-row: 1 / 2;
}
<div class="grid-container">
    <div class="grid-item">1</div>
    <div class="grid-item">2</div>
    <div class="grid-item">3</div>

    <div class="grid-item">4</div>
    <div class="grid-item">5</div>
    <div class="grid-item">6</div>
    
    <div class="grid-item">7</div>
    <div class="grid-item">8</div>
    <div class="grid-item">9</div>
</div>

https://ithelp.ithome.com.tw/upload/images/20210912/20001433CN8wZdaOoN.png

这种情况是最可怕的,由於隐性轨道跟一般网格轨道的功能是一样的,所以,当你原本设想的网格容器 3x3 的区域,如果因为设定错误,他就会变成 4x3 的网格容器。是的,网格系统很贴心的帮你把你要的区域给画出来了。

惊不惊喜,意不意外。

另外,在容器使用 grid-template-areas 的设定方式时,由於指定名称与其网格单元名称的配对不合(或没有配对),也会产生隐性格线,而且,在更复杂的网格单元设定时,更容易会造成不必要的隐性格线产生。

这边必须要先提及,grid-template-areas 有一个 附加特性

所有命名区块的位置,都会产生 4 条隐性命名格线,也就是栏(row)的方向两条,列(column)的方向两条。

我们先看范例,後面再来讲解到底为什麽。这个时候,我们还是请一只猫来切版,

.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar main main";
        
    width: 500px;
    height: 500px;
}

.nav {
    grid-area: nav;
}

.sidebar {
    grid-area: sidebarrrrr;
}

.main {
    grid-area: main;
}
<div class="grid-container">
    <nav class="grid-item nav">1</nav>
    <aside class="grid-item sidebar">2</aside>
    <main class="grid-item main">3</main>
</div>

最终的结果会是这样,

https://ithelp.ithome.com.tw/upload/images/20210912/200014337shofhNzk3.png

命名网格最可怕的地方在於,你的整个 Grid 容器尺寸不变,然後会依照比例 均分 这些网格格线所划分出来的区域,无论你是不是隐性网格格线都一视同仁。

之所以名字打错会造成这个问题,主要还是命名网格单元设定的 附加特性 有关。以上述的例子来看,我们单纯看栏(column)的部分就好,毕竟是正方形转过去就可以通了。

首先是 nav nav nav,在栏方向有两条线,名称分别是,

[nav-start], [nav-end]

在列方面也会有两条线,名称跟栏一样。其他的区块也分别都会有四组网格格线,如同我刚刚所描述的。那麽,我们再来看看那个不存在於命名区域设定的网格单元 sidebarrrrr

首先,他一样会有四条线,

栏方向 [sidebarrrrr-start], [sidebarrrrr-end]
列方向 [sidebarrrrr-start], [sidebarrrrr-end]

上面的计算结果,我们发现网格格线的计算数字被标记到了 6,原本只有 4 而已,原因是,你一个隐性命名网格单元,每个方向都会产生 2 条线,然而,他并不会将 上一个 网格单元的最後一条线做合并的动作,换句话说,

无论是一般命名网格单元还是隐性命名单元,他的网格格线都是独立的。

所以我们从栏方向来看 sidebarrr 这个单元,就很容易理解为何变成 6。因为对於 nav nav nav 这个命名区块来说,他的网格格线为 [nav-start], [nav-end],但由於隐性命名单元的发生,所以,单就 nav nav nav 这个区块来看,他的网格格线就变成了,

[nav-start], [nav-end], [sidebarrrrr-start], [sidebarrrrr-end]

由於 [nav-start] 等同於 网格格线数字 1,而 [nav-end]4,所以後面增加的隐性命名区块网格格线,就变成了 56 了。

当然,你不要打错名字就好了。

另外,如果在命名 Grid 单元上,使用了错误的网格区域设定,虽然不一定会造成隐性网格区域的产生,但一样会打坏原本网格单元摆放的逻辑。


使用命名格线与隐性格线的雷

前面有提到了格线可以取名字,但是不建议你的名字当中有 -start-end 结尾。原因在於,在 Grid 格线系统中,他会使用 -start-end 这两个关键字来组合你的网格名称,用以找到确切的网格格线位置。

又是一个小贴心,然後雷死你不偿命的。

我们举一个实际的例子来看,

.grid-container {
  display: grid;
  grid-template-columns: [first] 100px [foo foo-start] 100px [foo-end] 100px [last];
  grid-template-rows: repeat(2, 100px);
}

.grid-item:first-child {
    grid-column-start: first;
    grid-column-end: foo;
    
    grid-row: 1 / 2;
}

聪明如你,一定会觉得,阿不就是把第一个 .grid-item 放在第一格 100x100 的地方,这有什麽好困难的?

来,我们来看实际的结果,

https://ithelp.ithome.com.tw/upload/images/20210912/20001433QfBrD38bAk.png

惊不惊喜,意不意外。
我雷死你这小王八蛋!

这是网格格线一个不知道是不是因为隐性命名网格区域会用到 -start, -end 的关系,索性将这两个字也纳入了一般命名网格格线的规则里。为什麽?我上面之所以把 grid-column 分开来设定的原因,就恰巧可以解释这件事情。

  • grid-column-start 如果使用命名格线,他会去找看看有没有叫做 <我格线>-start 的命名格线,如果找不到才会先去找格线名字相同的,例如 <我格线>
  • grid-column-end 跟上面的逻辑一样逻辑同上,只是後面改成 <我格线>-end
  • grid-row-start, grid-row-end 逻辑同上。
  • grid-column, grid-row 这两个缩写的逻辑也同上。

所以,我上面的例子是这样命名的,

grid-template-columns: [first] 100px [foo foo-start] 100px [foo-end] 100px [last];

依据网格格线的规则,他会先去找 [foo-end],所以就会有上面图片中的效果。

为什麽不是先找 foo 而是先找 foo-end

w3c 官方说的,不爽你可以去他 Github 发 PR(欸

Line-based Placement
First attempt to match the grid area’s edge to a named grid area: if there is a grid line whose line name is -start (for grid--start) / -end (for grid--end), contributes the first such line to the grid item’s placement.

所以说,没事请不要乱用 -start-end 来命名你的格线,他会 优先配对

另外,如果使用了不存在的格线名称,那麽你的格线系统就会多一个尺寸为 0 的轨道,然後多一条线出来。跟刚才的命名单元一次多两条不一样。这个部分一样会破坏 Grid 原本摆放单元的位置,举例来说,

.grid-container {
  display: grid;
  grid-template-columns: [first] 100px [foo] 100px [boo] 100px [last];
  grid-template-rows: repeat(3, 100px);
}

.grid-item:first-child {
    grid-column-start: first;
    grid-column-end: qoo;
    
    grid-row: 1 / 2;
}

https://ithelp.ithome.com.tw/upload/images/20210912/20001433LfvD4b9Cke.png

总括来看,其实我并没有特别觉得网格系统很贴心,而这些真的只是为了维持网格展示能正常的一些补救(防呆防蠢不防雷)的作法。

对於不明就里的人来说,被雷到应该只是刚好。


小结

其实讲下来,应该跟格雷的阴影差不多。格线如果你都很规矩的操作,基本上不太容易出乱子。


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


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


<<:  [Day05] swift & kotlin 入门篇!(3) 基础语法-字串

>>:  Day5-Go变数介绍

[区块链&DAPP介绍 Day25] Dapp 实战 投票系统 - 1

今天我们藉着昨天的 第一个 Dapp 来稍微修改一下,改成一个投票系统。 首先我们先来尝试撰写这个投...

IT Certifications Exam Preparation

https://riich.me/blogs/view/13255/updated-amazon-a...

[ 卡卡 DAY 20 ] - React Native icon 用 react-native-vector-icons

react-native-vector-icons 是在看 React Native 所看到的 这...

架构介绍

对於资料、数据分析,已经有一点心得,但多半都停留在断断续续,不够紮实也不够完整,虽然可以弄出一个系统...

[第06天]理财达人Mx. Ada-下单作业

前言 本文说明如何进行下单作业。 程序实作 # 设定交易标的 # 以台股上市股票:长荣 contra...