display: grid 指北

display: flex

Flexible Box 模型,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。

说 flexbox 是一种一维的布局,是因为一个 flexbox 一次只能处理一个维度上的元素布局,一行或者一列。作为对比的是另外一个二维布局 CSS Grid Layout,可以同时处理行和列上的布局。

想要了解更多关于 display: flex 的信息,请参考MDN

下面三个小游戏可以帮你更快了解 Flex 布局的 核心概念:

网格布局:CSS Grid Layout

CSS Grid Layout(中文翻译:网格布局,也叫 CSS Grid 或 Grid) 引入了二维网格布局系统,可用于布局页面主要的区域布局或小型组件。

网格是一组相交的水平线和垂直线,它定义了网格的列和行。

浏览器支持性

Can I use display: grid
详细支持情况

术语

  • Grid Container:网格容器
  • Grid Item/Cell:网格元素
  • Grid Line:网格线
  • Grid Track:一行或一列
  • Grid Area: 一块区域(多个Cell组成的矩形)

网格容器

  1. 在元素上声明 display:griddisplay:inline-grid 可以创建一个网格容器。一旦这样做,这个元素的所有直系子元素将成为网格元素。

  2. grid-template-columns grid-template-rows

  • 使用空格分隔的值列表定义网格的列grid-template-columns和行grid-template-rows。这些值代表轨道大小,它们之间的空间代表网格线。
    Values:
    <track-size> – 长度 / 百分比 / fr
    <line-name> – 网格线别名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    .container {
    grid-template-columns: ... ...;
    /* e.g.
    1fr 1fr
    minmax(10px, 1fr) 3fr
    repeat(5, 1fr)
    50px auto 100px 1fr
    */
    grid-template-rows: ... ...;
    /* e.g.
    min-content 1fr min-content
    100px 1fr max-content
    */
    }
  • 网格线自动从 1 开始命名(-1为最后一根网格线,以此类推)

    网格线 - 自动命名
  • 可以手动指定别名:

    1
    2
    3
    4
    .container {
    grid-template-columns: [first] 40px [line2] 50px [line3] auto [col4-start] 50px [five] 40px [end];
    grid-template-rows: [row1-start] 25% [row1-end] 100px [third-line] auto [last-line];
    }
    网格线 - 别名
  • 同一根网格线可以有两个别名:

    1
    2
    3
    .container {
    grid-template-rows: [row1-start] 25% [row1-end row2-start] 25% [row2-end];
    }
  • repeat
    如果以相同的模式定义网格的行或列,可以使用 repeat 语法:

    1
    2
    3
    4
    5
    .container {
    grid-template-columns: repeat(3, 20px [col-start]);
    // 等价于
    grid-template-columns: 20px [col-start] 20px [col-start] 20px [col-start];
    }
  • fr
    fr 用来定义网格轨道大小的弹性系数。 每个网格轨道会按比例分配剩余的可用空间。当外层用一个 minmax() 表示时,它将是一个自动最小值(即 minmax(auto, <flex>) ) .

    1
    2
    3
    4
    5
    6
    .container {
    grid-template-columns: 1fr 1fr 1fr;

    // 也可以混合书写,这种模式下,计算的基数 = 父元素宽度 - 所有非弹性轨道的宽度
    grid-template-columns: 1fr 50px 1fr 1fr;
    }
  1. grid-template-areas
    用来定义网格区域 Grid areas
    Values:

    • <grid-area-name> : 网格区域的名称

    • . 表示空的网格单元格

    • none: 没有定义网格区域

      Examples:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      .container {
      display: grid;
      grid-template-columns: 50px 50px 50px 50px;
      grid-template-rows: auto;
      grid-template-areas:
      "header header header header"
      "main main . sidebar"
      "footer footer footer footer";
      }

      .item-a {
      grid-area: header;
      }
      .item-b {
      grid-area: main;
      }
      .item-c {
      grid-area: sidebar;
      }
      .item-d {
      grid-area: footer;
      }

      以上代码会创建一个 3 × 4 的网格布局:

      网格布局中的每一行包含的列数是相同的

      可以使用 . 声明空网格元素,多个连续的 ... 仍然代表一个空网格元素

      注意,这里没有使用网格线别名语法命名行,而是使用区域命名。
      当使用此语法时,区域两端的行实际上是自动命名的。如果网格区域的名称是 foo,那么该区域的起始行和起始列行的名称将是 foo-start,其最后一行和最后一列行的名称将是 foo-end。也就是某些行可能有多个名称,例如上面示例中的最左边的行,它将有三个名称: header-start、 main-start 和 footer-start。

  2. grid-template
    属性简写,用于同时定义 网格的行、列与分区

    所简写属性grid-template-areasgrid-template-rowsgrid-template-columns

    Values:

    • none
      关键词,设上文所简写属性none,即恢复默认设置。行列隐式生成,grid-auto-rowsgrid-auto-columns 定其尺寸。

    • <grid-template-rows> / <grid-template-columns>
      指定grid-template-rowsgrid-template-columns 的值,并设 grid-template-areasnone

    • [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?
      grid-template-areas 设置为<string>
      grid-template-rows 设置为<string>后面的<track-size>(为缺失的大小填充 auto
      在每个<track-size>之前/之后定义的轨道中拼接,
      grid-template-columns 设置为斜杠后指定的轨道列表(未指定则为none)。

      第三个写法较为复杂,可参考:MDN

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      .container1 {
      grid-template: none | <grid-template-rows> / <grid-template-columns>;
      }

      // 更复杂的语法
      .container2 {
      grid-template:
      [row1-start] "header header header" 25px [row1-end]
      [row2-start] "footer footer footer" 25px [row2-end]
      / auto 50px auto;
      }
      // 等价于
      .container3 {
      grid-template-rows: [row1-start] 25px [row1-end row2-start] 25px [row2-end];
      grid-template-columns: auto 50px auto;
      grid-template-areas:
      "header header header"
      "footer footer footer";
      }

      由于 grid-template 不会重置隐式网格属性(grid-auto-columnsgrid-auto-rowsgrid-auto-flow)多数情况下是符合预期的。使用 grid 属性可以简写更多属性,参考MDN

  3. column-gap | grid-column-gap, row-gap | grid-row-gap
    用来设置元素列(column-gap)或行(row-gap)之间的间隔 (gutter) 大小。
    Values:

    • <length> 定义行/列之间的间隔大小,非负数。

    • <percentage> 定义列之间的间隔大小,非负数的。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      .container {
      /* standard */
      column-gap: <line-size>;
      row-gap: <line-size>;

      /* old */
      grid-column-gap: <line-size>;
      grid-row-gap: <line-size>;
      }

      .container {
      grid-template-columns: 100px 50px 100px;
      grid-template-rows: 80px auto 80px;
      column-gap: 10px;
      row-gap: 15px;
      }

      间隔只会在列/行之间出现,不回出现在最外层元素与容器之间

      gap | grid-gap, 简写形式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      .container {
      /* standard */
      gap: <grid-row-gap> <grid-column-gap>;

      /* old */
      grid-gap: <grid-row-gap> <grid-column-gap>;
      }
      .container {
      grid-template-columns: 100px 50px 100px;
      grid-template-rows: 80px auto 80px;
      // 如果只提供一个值,则两个属性会被设置为相同值
      gap: 15px 10px;
      }
  4. justify-items, align-items
    定义网格元素在 (justify-items) 或 (align-items) 方向上的对齐方式

    1
    2
    3
    .container {
    justify-items: start | end | center | stretch;
    }

    justify-items 四个取值的效果展示:

    justify-items: start justify-items: end justify-items: center justify-items: stretch 该属性可被网格元素的 `justify-self` 属性重置。

    align-items 同理,只是方向换成了,增加了一个值:baseline,也可被网格元素的 align-self 属性重置

    place-items: 两个属性的简写,<align-items> / <justify-items>,如果只提供一个值,则同时设置两个属性。

  5. justify-content, align-content
    如果网格元素的总宽度比网格容器宽度小(所有元素都使用非弹性宽度指定,px),这种情况下,可以使用justify-content指定子元素的分布方式

    Values:

    • start

      justify-content: start
    • end

      justify-content: end
    • center

      justify-content: center
    • stretch

      justify-content: stretch
    • space-around: 在每个网格项目之间平均分配剩余空间,在网格容器与外层元素中间分配一半空间

      justify-content: space-around
    • space-between: 在每个网格项目之间平均分配剩余空间,在网格容器与外层元素中间不分配空间

      justify-content: space-between
    • space-evenly: 在每个网格项目之间(包括网格容器与外层元素中间)平均分配剩余空间

      justify-content: space-evenly

      align-content同理,只是方向换成了

      place-content: 简写形式,<align-content> / <justify-content>, 只提供一个值时表示同时设置两个属性。

  6. grid-auto-columns, grid-auto-rows
    grid-auto-columns指定隐式创建的网格纵向轨道(列)的宽度。
    grid-auto-rows指定隐式创建的网格横向轨道(行)的宽度。

    Values:

    • <track-size> – length / percentage / fr

      1
      2
      3
      4
      .container {
      grid-auto-columns: <track-size> ...;
      grid-auto-rows: <track-size> ...;
      }

      假设我们有如下网格容器布局:

      1
      2
      3
      4
      .container {
      grid-template-columns: 60px 60px;
      grid-template-rows: 90px 90px;
      }

      以及如下的网格元素:

      1
      2
      3
      4
      5
      6
      7
      8
      .item-a {
      grid-column: 1 / 2;
      grid-row: 2 / 3;
      }
      .item-b {
      grid-column: 5 / 6;
      grid-row: 2 / 3;
      }

      item-b 放置在第 5 列,直接使用了一个未定义的网格轨道,隐式创建的轨道默认宽度为0,我们可以通过 grid-auto-columnsgrid-auto-rows 来手动指定隐式创建的轨道宽度/高度。

      1
      2
      3
      .container {
      grid-auto-columns: 60px;
      }
  7. grid-auto-flow
    控制网格的自动布局算法怎样运作,精确指定在网格中被自动布局的元素怎样排列。

    Values:

    • row: 默认值,逐行填充,在必要时增加新行

    • column: 逐列填充,在必要时增加新列

    • dense: 该关键字指定自动布局算法使用一种 稠密 堆积算法,如果后面出现了稍小的元素,则会试图去填充网格中前面留下的空白。这样做会填上稍大元素留下的空白,但同时也可能导致原来出现的次序被打乱。
      如果省略它,使用一种稀疏算法,在网格中布局元素时,布局算法只会「向前」移动,永远不会倒回去填补空白。这保证了所有自动布局元素「按照次序」出现,即使可能会留下被后面元素填充的空白。
      denserow | column 组合会形成四种自动布局算法:

      1
      2
      3
      .container {
      grid-auto-flow: row | column | row dense | column dense;
      }
  8. grid
    简写形式,用来设置以下属性:

    • grid-template-rows
    • grid-template-columns
    • grid-template-areas
    • grid-auto-rows
    • grid-auto-columns
    • grid-auto-flow
    • grid-column-gap
    • grid-row-gap
      从易读易写的角度考虑,建议不要合并属性。更多详细信息请参考MDN

网格元素

float, display: inline-block, display: table-cell, vertical-align and column-* 不对网格元素生效。

  1. grid-column-start grid-column-end grid-row-start grid-row-end
    用来定义网格元素的起始列、结束列、起始行、结束行,从而确定网格元素在网格内的位置。
    Values:

    • <number> 起止于第几条网格线。

    • <name> 自定义的网格线的名称。

    • span <number> 表示当前网格会自动跨越指定的网格数量。

    • span <name> 表示当前网格会自动扩展,直到命中指定的网格线名称。

    • auto 全自动,包括定位,跨度等。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      .item {
      grid-column-start: <number> | <name> | span <number> | span <name> | auto;
      grid-column-end: <number> | <name> | span <number> | span <name> | auto;
      grid-row-start: <number> | <name> | span <number> | span <name> | auto;
      grid-row-end: <number> | <name> | span <number> | span <name> | auto;
      }
      .item-a {
      grid-column-start: 2;
      grid-column-end: five;
      grid-row-start: row1-start;
      grid-row-end: 3;
      }

      如果未指定grid-column-end/grid-row-end,网格元素默认会占据一个网格轨道宽度

      网格元素可以互相重叠,使用 z-index 控制层级顺序

  2. grid-column grid-row
    grid-column: 简写形式,grid-column-start + grid-column-end
    grid-row: 简写形式,grid-row-start + grid-row-end

    1
    2
    3
    4
    5
    6
    7
    8
    .item {
    grid-column: <start-line> / <end-line> | <start-line> / span <value>;
    grid-row: <start-line> / <end-line> | <start-line> / span <value>;
    }
    .item-c {
    grid-column: 3 / span 2;
    grid-row: third-line / 4;
    }

    如果只提供一个值,则网格元素默认占据一个轨道宽度/高度

  3. grid-area
    指定网格元素放在哪一个区域。区域由网格容器的 grid-template-areas 属性定义。
    同时也是 grid-row-start + grid-column-start + grid-row-end + grid-column-end 的简写形式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    .item {
    grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
    }
    .item-a {
    grid-area: header;
    }
    .item-b {
    grid-area: 1 / col4-start / last-line / 6;
    }
  4. justify-self align-self place-self
    与网格容器的 justify-items, align-items, place-items 属性作用相同,但是仅作用于单一网格元素,不再赘述。

其他

  1. fr
    前面已经提过一次,在网格布局中经常会用到 fr 单位,了解更多,用来表示占据剩余空间的一部分。例如:

    1
    grid-template-columns: 1fr 3fr;

    表示这两列宽度分别为 25% 75%。

  2. 设置网格轨道宽度时,除了px rem % 等常用长度单位,还可以使用:

    • min-content: 最小内容宽度
    • max-content: 最大内容宽度
    • auto: 效果与 1fr 非常类似,但是遇到相邻轨道
    • fit-content: 介于min-contentmax-content之间,尽量占据更多空间。
    • minmax(min, max): 大于等于min值,并且小于等于max值。如果max值小于min值,则该值会被视为min值。最大值可以设置为网格轨道系数值<flex> ,但最小值则不行。
  3. repeat
    需要定义重复的网格轨道时,使用repeat可以节省字符:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    .grid {
    grid-template-columns:
    1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

    /* 更简洁: */
    grid-template-columns:
    repeat(8, 1fr);

    /* 当一个网格轨道宽度更复杂时: */
    grid-template-columns:
    repeat(8, minmax(10px, 1fr));

    /* 与关键字结合 */
    grid-template-columns:
    repeat(auto-fill, minmax(250px, 1fr));

    grid-template-columns:
    repeat(auto-fit, minmax(250px, 1fr));
    }
  4. 瀑布流布局
    网格布局标准支持了一个瀑布流布局语法:

    1
    2
    3
    .grid {
    grid-template-rows: masonry;
    }

    但是目前没有浏览器实现,firefox 可以通过 flag 开启支持
    can i use, grid-template-rows: masonry

  5. 动画
    根据标准(CSS Grid Layout Module Level 1 specification),以下5个属性可以在动画中使用:

    • grid-gap, grid-row-gap, grid-column-gap: 值为 length, percentage 或 calc 表达式

    • grid-template-columns, grid-template-rows: 值为 length, percentage 或 calc 表达式 的列表,并且唯一变化属性是length, percentage 或 calc 表达式

      具体的支持情况:

      Browser (grid-)gap(grid-)row-gap(grid-)column-gap grid-template-columns grid-template-rows
      Firefox supported ✅ 53+ supported ✅ 66+ supported ✅ 66+
      Safari 12.0 not supported ❌ not supported ❌ not supported ❌
      Chrome supported ✅ 66+ not supported ❌ not supported ❌
      Chrome for Android 66+, Opera Mini 33+ supported ✅ not supported ❌ not supported ❌
      Edge supported ✅ 16+ not supported ❌ not supported ❌

参考链接

写给自己看的display: grid布局教程, by 张鑫旭
CSS Grid 网格布局教程, by 阮一峰
A Complete Guide to Grid, by Chris House

挑战

https://codepen.io/zhangzhen/pen/wvrvvMQ
fork 并完成代码,使页面布局尽可能长得像下图:
Assignment