CSS Grid 那些鲜为人知的内幕

ops/2024/11/9 0:36:25/

叔本华:人生没有意义,人生就是一团欲望,欲望满足了就空虚,满足不了就痛苦,人生就像摆钟在空虚和痛苦中来回摇摆。

大家好,我是「柒八九」。一个「专注于前端开发技术/RustAI应用知识分享」Coder

前言

大家还记得我们之前介绍过的CSS_Flex 那些鲜为人知的内幕,在文章中我们不是对API的罗列,而是从内部原理方向来解析Flex中我们常见的属性和使用方式。该篇文章也得到大家的一致好评。

而今天,我们来讲讲我们平时可能会忽略,但是在一些应用场景中能让我们得心应手的另外的布局方式 - Grid

还是和上一篇Flex文章一样,我们不是对Grid的API进行罗列,而是从更深层次的角度来了解Grid。也就是意味着,本篇文章需要一定的Grid的基础知识。如果大家还不了解,可以翻看阮一峰老师写的CSS Grid 网格布局教程[1]

好了,天不早了,干点正事哇。

alt

我们能所学到的知识点

  1. Gird 是个啥
  2. Grid 是重要的布局算法之一
  3. 开启 Grid 布局
  4. 创建网格单元
  5. 分配子项
  6. 对齐方式

1. Grid 是个啥

网格布局(Grid)将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。

alt

上图这样的布局,就是 Grid 布局的拿手好戏。

Grid vs Flex

Grid 布局与 Flex 布局有一定的相似性,都可以指定「容器」内部多个「项目」的位置。但是,它们也存在重大区别。

  • Flex 布局是 「轴线布局」,只能指定 项目针对轴线的位置,可以看作是 「一维布局」
  • Grid 布局则是将 容器划分成 ,产生单元格,然后指定 项目所在的单元格,可以看作是 「二维布局」

Grid 布局远比 Flex 布局强大。

Grid 相关术语

容器

容器是应用了 display: grid 样式的元素。它是所有网格项的「直接父元素」

<div class="container">
  <div class="item item-1"> </div>
  <div class="item item-2"> </div>
  <div class="item item-3"> </div>
</div>

在这个例子中,.container所对应的元素就是就是容器

项目

项目是网格容器的子元素(即「直接后代」)。

<div class="container">
  <div class="item"> </div>
  <div class="item">
    <p class="sub-item"> </p>
  </div>
  <div class="item"> </div>
</div>

在这个例子中,item 元素是项目,但 sub-item 不是。

网格线

网格线是构成网格结构的分割线。它们可以是垂直的(列网格线)或水平的(行网格线),并位于行或列的两侧。

alt

在这里,黄色线是列网格线的一个例子。

网格单元

网格单元是两个相邻的行网格线和两个相邻的列网格线之间的空间。它是网格的单个「单位」

alt

在这个例子中,这是位于行网格线 1 和 2 之间,以及列网格线 2 和 3 之间的网格单元。

轨道

轨道是两个相邻网格线之间的空间。

我们可以将它们看作是网格的列或行。

alt 在这个例子中,这是第二行网格线和第三行网格线之间的轨道

网格区域

网格区域是由四条网格线围成的总空间。

一个网格区域可能由「任意数量的网格单元组成」

alt

在这个例子中,这是位于行网格线 1 和 3 之间,以及列网格线 1 和 3 之间的网格区域。

容器上的API

alt

项目上的API

alt

浏览器支持

根据 caniuse[2]Grid 支持 97.78% 的用户。

alt

2. Grid 是重要的布局算法之一

在我们构建复杂页面时,就会用到各种各样的布局算法,每种算法用于不同类型的用户界面。如下图: alt

  • Flow布局[3]是浏览器「默认的布局算法」,设计用于数字文档。 alt alt

  • Flexbox 设计用于沿单个轴分配项目,这个我们在CSS_Flex 那些鲜为人知的内幕有过介绍

  • Grid是我们今天的主角

  • Position[4]用于设计一些脱离文档流的元素 alt

  • Table布局[5]设计用于表格数据

  • Float[6]用于设计一些文本环绕的布局 alt

相比,我们比较熟悉的布局算法(flaot/position/table等)Grid 是最新最强大的布局算法。grid是2017年才发布的。

Grid最令人神往的地方就是它的网格结构,即行和列,具体表现就是这些页面布局只需在 CSS 中定义即可。

下面的页面结构是我们常见的「圣杯布局」

<header></header>
<nav></nav>
<main></main>
<footer></footer>
alt

使用 Grid来实现该布局,我们只需要在CSS中划分好具体哪个元素所占的区域即可。(这里我们就不贴代码了)

而在其他任何布局模式中,创建这样的区块的唯一方法就是「添加更多的 DOM 节点」。例如,在表格布局中,每行都是用 <tr> 创建的,每个行中的单元格则使用 <td><th>

<table>
  <tbody>
    <!-- 第一行 -->
    <tr>
      <!-- 第一行中的单元格 -->
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <!-- 第二行 -->
    <tr>
      <!-- 第二行中的单元格 -->
      <td></td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
</table>

与其他布局不同,Grid 允许我们完全在 CSS 中管理布局。我们可以将容器切成任意形状,然后将子元素和这些区块对应即可。


3. 开启 Grid 布局

我们通过 display 属性选择启用网格布局模式:

.container {
  display: grid | inline-grid;
}
  • grid – 生成块级网格
  • inline-grid – 生成内联级网格

默认情况下,Grid 使用「单列」,并根据子元素的数量动态创建行。这被称为「隐式网格」,因为我们没有明确定义任何结构。

alt
alt

隐式网格是动态的;根据子元素的数量将添加和删除行。每个子元素都有自己的行。

默认情况下,网格容器的高度由其子元素确定。

它会动态增长和收缩。其实,网格容器仍然使用流式布局,而流式布局中的块级元素会垂直增长以容纳其内容。「只有子元素使用网格布局进行排列」

容器高度固定

当我们将容器的高度固定后,在这种情况下,其内部项目的高度会「均分」容器高度。也就是当拥有多个项目时它们被分成大小相同的行。

alt
alt

4. 创建网格单元

默认情况下,Grid将创建单列布局。我们可以使用grid-template-columns[7]属性指定列:

alt

通过将两个值传递给grid-template-columns —— 25%75% —— 告诉Grid算法将元素分成两列。

可以使用任何有效的CSS <length-percentage>值定义,包括像素rems视口单位等。此外,我们还可以使用新的单位,即fr单位[8]

这里多说一句,在CSS Values and Units Module Level 4[9]中定义了关于length的值

alt
alt

这里的fr代表分数(fraction)。在这个示例中,我们说第一列应该占用1个单位的空间,而第二列占用3个单位的空间。这意味着总共有4个单位的空间,这成为分母。第一列占据了可用空间的1/4,而第二列占据了3/4

fr vs %

fr单位为Grid带来了类似Flexbox样式的灵活性。百分比<length> 值会创建硬约束,而fr列可以「根据需要自由地增长和收缩,以容纳其内容」

案例1

仔细观看下面的例子,Grid的项目一个用了fr一个用了%。此时我们为第一列的头像赋予了一个指定宽度的图像。随着容器宽度发生变化,当容器宽度小到一定程度,即第一列的宽度小于图像的设定宽度时,就会发生如下的变化。

  • 基于 百分比的列的宽度大小会按照 容器宽度*N%变化,当列宽度小于图像宽度时,图像从列中溢出。 alt
  • 基于 fr单位的列无论如何缩小容器宽度,该列也不会收缩到其最小内容大小以下。 alt

更准确地说:fr单位分配额外的空间。首先,列宽将根据其内容计算。如果有剩余空间,它将根据fr值进行分配。该特性和flex-grow是一致的。

案例2

我们再来用一个例子来说明fr%的区别。此时我们用gap来设置所有列和行之间添加了固定量的空间

看看在%fr之间切换时会发生什么:

alt
alt

当使用基于%的列时,内容会溢出到网格父容器之外。这是因为%是使用总网格区域来计算的。这两列消耗了父容器的内容区域的25%+75%=100%,并且它们不允许收缩。当我们添加了16pxgap时,列别无选择,只能溢出容器。

相比之下,fr「基于额外的空间计算」的。在这种情况下,额外的空间已经减少了16px,以用于设置gap


隐式和显式行

隐式行

如果我们向一个两列网格添加「超过两个子元素」会发生什么呢?

alt

从结果来看,gird将第三个元素放置到了第二行。

grid算法希望确保「每个子元素都有自己的网格单元」。它会根据需要「生成新的行来实现这个目标」

这在我们有可变数量的项目并且我们希望容器自动排布项目的情况下非常方便。

显式行

不过,在其他情况下,我们希望「显式定义行,以创建特定的布局」。我们可以使用grid-template-rows[10]属性来实现:

alt

通过同时定义grid-template-rowsgrid-template-columns,我们创建了一个显式网格。我们就可以用几行代码,实现了所谓的「圣杯布局」


repeat

假设我们正在构建一个日历:

alt

Grid是处理这种情况的绝佳工具。我们可以将其构建为一个7列的网格,每列占据1个单位的空间:

.calendar {
  display: grid;
  grid-template-columns1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

上面方式肯定是有效可行的,但是我们不想重复写1fr多次。此时我们就可以使用repeat()来解决。

.calendar {
  display: grid;
  grid-template-columnsrepeat(71fr);
}

repeat函数会为我们进行复制和粘贴。


5. 分配子项

默认情况下,Grid算法会将每个子项分配给「第一个未占用的网格单元」

但是呢,Grid还赋予我们一种能力-我们可以将我们的项目分配到任何我们想要放置的单元格!子项甚至可以跨越多行/列

alt

grid-row[11]grid-column[12]属性允许我们指定网格子项应该占据哪些轨道

如果我们希望子项占据单个行或列,我们可以通过其编号来指定。grid-column: 3将使子项位于第三列。

alt

网格子项还可以跨越多个行/列。其语法「使用斜杠来划分起始和结束位置」

.child {
  grid-column1 / 4;
}

上面的1 / 4可不是一个分数,在CSS中,「斜杠字符不用于除法,而是用于分隔值组」。在这种情况下,它允许我们在一个声明中设置起始和结束列。

这本质上是这样的简写形式:

.child {
  grid-column-start1;
  grid-column-end4;
}

我们提供的数字是「基于列线」,而不是列索引。

alt

一个有4列的网格实际上有5条列线。当我们将子项分配到网格时,我们使用这些线来锚定它们。如果我们希望子项跨越前3列,它需要从第1行开始,并在第4行结束。

alt

负数行号

从左到右的语言中,比如英语,我们从左到右计算列。然而,使用负数行号,我们也可以反向计算,从右到左

.child {
  /* 位于从右数的第2列: */
  grid-column: -2;
}

我们还可以混合使用正数和负数。

alt
alt

对比上面两个例子,尽管我们根本没有改变grid-column的配置(grid-column:1 /-1),虽然列数增加了,但是每个例子中的子项都跨越了网格的整个宽度!


areas

假设我们正在构建这个布局:

alt

根据我们目前学到的知识,我们可以这样操作:

.grid {
  display: grid;
  grid-template-columns2fr 5fr;
  grid-template-rows50px 1fr;
}
.sidebar {
  grid-column1;
  grid-row1 / 3;
}
header {
  grid-column2;
  grid-row1;
}
main {
  grid-column2;
  grid-row2;
}

上面例子是可行的,但是Grid还为我们提供了更好的解决方案 - grid-areas[13]

alt
alt
alt

像之前一样,我们使用 grid-template-columnsgrid-template-rows 定义了网格结构。除此之外,我们还使用grid-template-areas定义了一个区域的划分

.parent {
  grid-template-areas:
    'sidebar header'
    'sidebar main';
}

使用grid-template-areas我们勾勒出了我们想要创建的网格。

每一行代表一行,每个单词是我们给网格的特定部分命名。

然后,我们不是用 grid-columngrid-row 分配子项,而是用 grid-area[14]

当我们想让特定区域跨越多行或多列时,我们可以在我们的模板中「重复该区域的名称」。在这个例子中,sidebar区域跨越了两行,所以我们在第一列的两个单元格中都写了 sidebar

如何抉择

在构建显示布局时,我们可以通过使用areas行/列都可以达到目的,但是呢,使用areas时,它允许我们给grid分配语义含义,而不是使用晦涩难懂的行/列数字。也就是说,当网格具有固定数量的行和列时,areas效果最佳。grid-columngrid-row 可以在隐式网格中很有用。


键盘用户的注意事项

在处理网格分配时存在一个重要的问题:Tab 键顺序仍然基于 DOM 位置,而不是网格位置。

通过一个示例会更容易理解。在这个示例中,我设置了一组按钮,并使用 Grid 对它们进行了排列:

alt

如果我们使用的是带有键盘的设备,可以通过点击左上角的第一个按钮(One),然后按 Tab 键逐个移动按钮。

你应该会看到类似于这样的情况:

alt

焦点轮廓在页面上毫无规律地跳动,这是因为按钮的焦点是「基于它们在 DOM 中出现的顺序而定」的。

为了解决这个问题,我们应该重新按视觉顺序在 DOM 中重新排列网格子项,以便我可以从左到右,从上到下进行 Tab 键浏览。


6. 对齐方式

justify-content

到目前为止我们看到的所有示例中,我们的列和行都会伸展以填满整个网格容器。然而,我们是通过配置让内容进行别样的排布。

  • start:将网格与容器的开始边缘对齐
  • end:将网格与容器的结束边缘对齐
  • center:将网格置于容器的中心
  • stretch:重新调整网格项的大小,以使网格填充容器的整个宽度
  • space-around:在每个网格项之间放置相等量的空间,两端的空间为一半大小
  • space-between:在每个网格项之间放置相等量的空间,两端没有空间
  • space-evenly:在每个网格项之间放置相等量的空间,包括两端

例如,假设我们定义了两个都是 90px 宽的列。只要网格容器大于 180px,就会有一些多余的空间:

alt

如果想利用多余空间进行对项目的排布处理,此时我们可以使用 justify-content 属性来控制列的分布,并且我们接受上面所列举的各种值。

.container {
  justify-content: start | end | center | stretch | space-around | space-between | space-evenly;    
}
justify-content:start
justify-content:start
justify-content:center
justify-content:center
justify-content:end
justify-content:end
justify-content:space-between
justify-content:space-between
justify-content:space-around
justify-content:space-around
justify-content:space-evenly
justify-content:space-evenly

看到space-between/space-around是否想到Flex,布局排布的原理是一样的,只不过GridFlex最大的区别在于,我们正在「对齐列,而不是项本身」。本质上,justify-content[15] 让我们更好的操作网格的列,以便可以根据我们的意愿将它们分布在整个网格中。

justify-items

如果我们想在列内对齐项目本身,我们可以使用 justify-items 属性:

  • start:将项目与其单元格的开始边缘对齐
  • end:将项目与其单元格的结束边缘对齐
  • center:将项目置于其单元格的中心
  • stretch:填充单元格的整个宽度(这是默认值)
.container {
  justify-items: start | end | center | stretch;
}

当我们将一个 DOM 节点放入网格父元素时,默认行为是它会跨越整个列,就像流式布局中的 <div> 会横向拉伸以填满其容器一样。但是,使用 justify-items,我们可以调整这种行为。

.container {
  justify-items: stretch;
}
alt
.container {
  justify-items: start;
}
alt
.container {
  justify-items: end;
}
alt
.container {
  justify-items: center;
}
alt

justify-self

我们可以使用justify-self来控制「特定网格子元素」的对齐方式

其值为以下几个:

  • start:将网格项与其单元格的开始边缘对齐
  • end:将网格项与其单元格的结束边缘对齐
  • center:将网格项置于其单元格的中心
  • stretch:填充单元格的整个宽度(这是默认值)
.item {
  justify-self: start | end | center | stretch;
}
.item-a {
  justify-self: start;
}
alt
.item-a {
  justify-self: end;
}
alt
.item-a {
  justify-self: center;
}
alt
.item-a {
  justify-self: stretch;
}
alt

垂直方向的对齐处理

到目前为止,我们一直在讨论如何在水平方向上对齐内容。Grid 还提供了一组额外的属性来在垂直方向上对齐内容:

align-items

其取值为以下几种:

  • stretch:填充单元格的整个高度(这是默认值)
  • start:将项目与其单元格的开始边缘对齐
  • end:将项目与其单元格的结束边缘对齐
  • center:将项目置于其单元格的中心
  • baseline:沿着文本基线对齐项目。
.container {
  align-items: start | end | center | stretch;
}

示例

.container {
  align-items: start;
}
alt
.container {
  align-items: end;
}
alt
.container {
  align-items: center;
}
alt
.container {
  align-items: stretch;
}
alt

总结

align-content 类似于 justify-content,但它影响的是行而不是列。同样,align-items 类似于 justify-items,但它处理的是网格区域内项目的垂直对齐,而不是水平对齐。

为了进一步梳理:

  • justify — 处理列
  • align — 处理行
  • content — 处理网格结构
  • items — 处理网格结构内的 DOM 节点。

最后,除了 justify-self,我们还有 align-self。这个属性控制单个网格项在其单元格内的垂直位置。

place-content

place-content 属性是一个缩写。它是这样的语法糖:

.parent {
  justify-content: center;
  align-content: center;
}

使用该属性,我们可以用最少的代码实现我们平时很难实现的布局。

只使用两个 CSS 属性,我们就可以将子元素水平和垂直居中于容器中: alt

正如我们所学到的,justify-content 控制列的位置。align-content 控制行的位置。在这种情况下,我们有一个隐式网格只有一个子元素,因此我们得到一个 1×1 网格。place-content: center 将行和列都推向中心。

将元素放置在左上角
将元素放置在左上角
将元素放置在右下角
将元素放置在右下角

后记

「分享是一种态度」

「全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。」

alt

Reference

[1]

CSS Grid 网格布局教程: https://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html

[2]

caniuse: https://caniuse.com/css-grid

[3]

Flow布局: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flow_layout

[4]

Position: https://developer.mozilla.org/en-US/docs/Web/CSS/position

[5]

Table布局: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_table

[6]

Float: https://developer.mozilla.org/en-US/docs/Web/CSS/float

[7]

grid-template-columns: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns

[8]

fr单位: https://www.digitalocean.com/community/tutorials/css-css-grid-layout-fr-unit

[9]

CSS Values and Units Module Level 4: https://drafts.csswg.org/css-values/#lengths

[10]

grid-template-rows: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-rows

[11]

grid-row: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row

[12]

grid-column: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column

[13]

grid-areas: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-areas

[14]

grid-area: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-area

[15]

justify-content: https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content

本文由 mdnice 多平台发布


http://www.ppmy.cn/ops/24748.html

相关文章

报错:图片验证码接口对接vue+springboot(下一个笔记会记录整个验证码的代码)

问题&#xff1a;空指针异常ai: 根据错误堆栈信息中提供的方法调用位置&#xff0c;看起来空指针异常是在 AuthCodeServiceImpl 类的 authUserCoded 方法的第 41 行发生的。 为了解决这个问题&#xff0c;你可以检查 AuthCodeServiceImpl 类中 authUserCoded 方法的第 41 行&am…

从零手写实现 apache Tomcat-01-入门介绍

创作缘由 平时使用 tomcat 等 web 服务器不可谓不多&#xff0c;但是一直一知半解。 于是想着自己实现一个简单版本&#xff0c;学习一下 tomcat 的精髓。 怎么实现一个 tomcat 呢&#xff1f; Tomcat就像是一个用Java语言搭起来的大舞台&#xff0c;专门用来演出那些用Jav…

基于ESP32—CAM物联网WIFI小车

一.功能概述 摄像头的画面可以实时的传输到,点灯科技APP的手机端,这样可以实时查看周围环境的状况,灯光不足,画面不清晰时可以打开灯光照明。手机端有左转、右转、前进、后退、停止的按钮。可以根据自己需要,来控制小车。手机APP端还设有模式切换的按钮,可以根据需要进行…

备考数通HCIE证书4点经验分享!

大家好&#xff0c;我是来自安阳工学院20级网络工程的刁同学&#xff0c;在2023年12月20日成功通过了华为Datacom HCIE认证&#xff0c;并且取得了笔试900多分&#xff0c;实验B的成绩。在此&#xff0c;我想把我的一些考证心得分享给正在备考的小伙伴们。 关于为什么考证 我…

C#面:ASP.NET 与 ASP 相比,主要有哪些进步

C# ASP.NET 相对于传统的ASP有以下几个主要的进步&#xff1a; 更强大的编程语言&#xff1a;C#是一种现代化的编程语言&#xff0c;相比于ASP使用的 VBScript 或 JScript&#xff0c;C#具有更强大的面向对象编程能力和更丰富的语法特性&#xff0c;使得开发人员可以更高效地编…

C++:初始C++

文章目录 C关键字命名空间命名空间定义命名空间的使用 C的输入&输出缺省参数缺省参数的概念缺省参数分类 函数重载函数重载的概念C支持函数重载的原理——名字修饰(name Mangling) 引用引用的概念引用特性常引用使用场景做参数做返回值 传值、传引用效率比较值和引用作为返…

Seata-server配置

首先先查看一下版本看看所用的版本是否都兼容 版本兼容查看 建立seata-server数据库 数据库DDL 给每个业务库建立undo.log表 undo.log 然后在虚拟机安装seata-server 创建文件路径&#xff0c;并创建docker-compose.yml文件 创建完成后先启动一下seata docker run -rm seata…

数据库同步革命:MySQL GTID模式下主从配置的全面解析

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 数据库同步革命&#xff1a;MySQL GTID模式下主从配置的全面解析 前言GTID模式简介常用配置参数GTID复制监控与管理1. 监控GTID复制状态和延迟MySQL内置状态查询&#xff1a;外部监控工具&#xff1a;…