前言
📫 大家好,我是陈三心,热爱技术和分享,欢迎大家交流,一起学习进步!
🍅 个人主页:陈三心
背景
最近在项目中遇到了这样的一个响应式布局需求:
在不同屏幕宽度下,动态调整卡片内容的列数,并且每列的宽度不能小于 300px,同时要充分利用剩余空间。
最初,我尝试使用 Flex 布局来实现这一需求,但很快发现 Flex 虽然强大,却无法完美应对这种动态列数和固定最小宽度的场景。
正当我为此头疼时,团队中的一位伙伴提到了 CSS Grid 布局。之前对 Gird 布局仅仅局限于了解程度,并没有去深入学习,因为大部分场景使用 Flex 布局就足矣完成。既然 Flex 在该需求场景下显得有些力不从心,于是,我开始研究 Grid 的相关文档,发现其功能远比我想象的要强很多,它不仅让代码更加简洁,还提供了更强大灵活的布局控制能力。最终,通过 Grid 布局完美解决了我的问题。
实现代码其实非常简单:
.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr) ) ;
}
上述代码中的repeat、minmax和fr是什么呢?别着急,看完本篇你将对 Grid 有一个全面的认识。
Grid 布局简介
想象一下,你在设计一个网页布局时,就像在画一张表格。你可以把网页分成很多行和列,形成一个网格系统。Grid 布局就是这样一个强大的工具,它允许你通过定义行和列来创建复杂的网页布局。你可以把内容(比如图片、文字)放到这些网格的任意位置,甚至可以跨越多行或多列。
简单来说,Grid 布局(网格布局)就是将一个网页划分成一个一个的小格子,然后你可以自由地把内容放到这些格子里,或者让内容跨越多个格子。它就像是一个灵活的“网格画布”,让你可以轻松地控制网页的布局结构。
Grid 布局的优势:
-
二维布局:Grid 布局是真正的二维布局,可以同时控制行和列。相比于 Flex(主要是一维布局,只能控制行或列),Grid 布局更适合处理复杂的网页布局。
-
灵活控制:通过简单的 CSS 属性,可以精确控制每个网格项目的位置和大小。
-
代码简洁:相比传统的布局方法,Grid 布局几行 CSS 就能实现复杂的布局效果,更加简洁和易于维护。
浏览器兼容性
目前,所有现代浏览器都支持 CSS Grid 布局,包括 Chrome、Firefox、Safari、Edge 等。
根据caniuse,97.21%的用户支持Grid,即 Grid 在现代浏览器中已经非常成熟,可以放心使用。
基本概念
1. 容器和项目
-
容器:采用网格布局的区域,即为容器。
-
项目:容器里采用网格定位的直系子元素就是“项目”。
2. 行和列
-
行:容器中的水平区域,把容器分成几行。
-
列:容器中的垂直区域,把容器分成几列。
3. 单元格和网格线
-
单元格:行和列交叉形成的小格子,就是“单元格”。
-
网格线:划分行和列的线就是“网格线”。
想象你在画一个九宫格:
-
容器就是整个九宫格。
-
行和列把九宫格分成 3 行 3 列(图中黄色区域)。
-
单元格就是九宫格里的每一个小格子。
-
网格线就是划分九宫格的横线和竖线(蓝色区域)。共有4根水平网格线和4根垂直网格线。
你可以把内容(项目)放到任意一个格子里,或者让内容跨越多行或多列。这就是 Grid 布局的核心思想!
在 Grid 布局中,容器属性和项目属性是构建网格系统的核心。下面来详细介绍。
容器属性
容器属性是指定义在容器上的属性,用于定义网格的整体结构。
display 属性
display 属性用于将元素定义为网格容器,只需要将其设置为 grid 即可:
.container {
display: grid;
}
默认情况下,Grid 的列是独占一行的,其宽度随父盒子大小。效果如下:
上述的容器元素是块级元素,但也可以设成行内元素。
-
inline-grid
:生成行内网格容器。
.container {
display: inline-grid;
}
grid-template-columns && grid-template-rows
grid-template-columns 和 grid-template-rows 是 Grid 的核心属性,用于定义网格的列宽和行高。
.container {
display: grid;
grid-template-columns: 50px auto 100px;
grid-template-rows: 33.33% 33.33% 33.33%;
}
上述代码定义了一个简单的 3x3 网格布局,左侧和右侧列宽固定,中间列宽自适应,行高均匀分布,效果如下:
fr 关键字
除了通过px、%等单位来设置网格的宽度,网格布局提供了一个新的 fr 关键字(fraction 的缩写,意为"片段"),来表示比例关系。
.container {
display: grid;
grid-template-columns: 100px 1fr 3fr;
}
上述代码表示第一列宽度为 100px,后面两列的宽度分别为剩余宽度的 1/4 和 3/4。
你可能会有疑问?这个 fr 和百分比有啥区别?别着急,来看看下面设置了百分比的效果。
.container {
display: grid;
grid-template-columns: 100px 25% 75%;
}
上述例子中,我们可以发现,在设置百分比后,出现了一个滚动条。这也就是 fr 和 % 的区别:
- fr:表示剩余空间的分配比例。
- %:表示相对于父容器宽度的固定比例。
repeat()
有时候,重复写同样的值非常麻烦,尤其网格很多时。这时,可以使用 repeat() 函数,来简化重复定义列或行的代码。
它接受两个参数,第一个参数是重复的次数,第二个参数是所要重复的值。
例如,定义一个 3 列的网格,每列等宽:
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
等效于
.container {
grid-template-columns: 1fr 1fr 1fr;
}
auto-fill 关键字
有时,单元格的大小是固定的,但是容器的大小不确定。如果希望每一行(或每一列)容纳尽可能多的单元格,这时可以使用 auto-fill 关键字表示自动填充。
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 300px);
}
上述代码表示每列宽度300px,然后自动填充,直到容器不能放置更多的列。
等等这不就是文章开头例子的效果吗?没错,已经很接近了,只需再给每列设置最小值和最大值。
auto-fit 关键字
除了 auto-fill,还有一个关键字 auto-fit,两者的行为基本是相同的。只有当容器足够宽,可以在一行容纳所有单元格,并且单元格宽度不固定的时候,才会有差异。auto-fill 会用空格子填满剩余宽度,auto-fit 则会尽量扩大单元格的宽度。
假设容器宽度为 500px,每列最小宽度为 100px,内容只有 3 个项目:
auto-fill:
[100px] [100px] [100px] [100px] [100px]
创建 5 列,其中 2 列为空白。
auto-fit:
[166px] [166px] [166px]
创建 3 列,空白列被折叠,剩余空间均匀分配给 3 列。
minmax()
minmax() 设置每列的最小值和最大值,可以确保网格在响应式布局中既不会过小,也不会过大。
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr) ) ;
上述代码(即文章开头代码)中,minmax(300px, 1fr) 表示列宽不小于300px,不大于1fr。
minmax() 搭配 repeat() 和 auto-fill,可以轻松创建动态、自适应的网格布局!
grid-row-gap && grid-column-gap && grid-gap
grid-row-gap 用于设置行间距,grid-column-gap 用于设置列间距。
.container {
grid-row-gap: 10px;
grid-column-gap: 10px;
}
Grid提供了两者的合并简写形式 grid-gap,同时定义行间距和列间距。上面代码等同于下面代码:
.container {
grid-gap: 10px 10px;
}
在现代浏览器中,相关属性已经进行了简化,grid-
前缀被移除,推荐使用更简洁的属性名:
-
gap:代替 grid-gap。
-
row-gap:代替 grid-row-gap。
-
column-gap:代替 grid-column-gap。
到此,再来看下开头例子的完整代码:
.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr) ) ;
}
是不是发现都已经掌握了呢。接下来让我们继续学习 Grid 布局的其它属性。
grid-auto-flow 属性
grid 容器的子元素默认会按照先行后列的顺序放置,即先填满第一行,再开始放入第二行。
这个顺序由 grid-auto-flow 属性决定,默认值是 row。也可以将它设成 column,变成先列后行。
除了常见的 row 和 column 外,还可以设置为 row dense 和 column dense。这两个值主要用于在某些项目指定位置后,剩下的项目如何自动填充空白区域。
-
row dense
:按行排列,并尝试填充空白区域。 -
column dense
:按列排列,并尝试填充空白区域。
下面的例子让1号项目和2号项目各占据两个单元格,然后在默认的 grid-auto-flow 为 row 的情况下,会产生下面这样的布局。
上述会出现空白区域,而 dense 会尝试让剩余项目填充这些空白,而不是严格按顺序排列。
grid-auto-flow: row dense;
效果如下:
上图会先填满第一行,再填满第二行,所以3号项目就会紧跟在1号项目的后面,4号项目就会排到第二行。
justify-items && align-items && place-items
这些属性用于控制网格项在单元格内的对齐方式。
justify-items:
控制网格项在单元格内的水平对齐方式。
align-items:
控制网格项在单元格内的垂直对齐方式。
place-items:
justify-items 和 align-items 的简写形式。
justify-items 和 align-items 这两个属性值相同,都可以取下面这些值。
.container {
justify-items: start | end | center | stretch;
align-items: start | end | center | stretch;
}
-
start
:对齐到单元格的起始位置。 -
end
:对齐到单元格的结束位置。 -
center
:单元格居中对齐。 -
stretch
:拉伸以占满单元格的整个宽度(默认值)。
也可使用它们的合并简写形式。
place-items: <align-items> <justify-items>;
1.start
:对齐到单元格的起始位置
.container {
justify-items: start;
}
2.end
:对齐到单元格的结束位置
.container {
justify-items: end;
}
3.center
:单元格居中对齐。
.container {
justify-items: center;
}
4.stretch
:拉伸以占满单元格的整个宽度
.container {
justify-items: stretch;
}
justify-content && align-content && place-content
这些属性用于控制整个网格内容在容器内的对齐方式。
justify-content:
控制网格内容在容器内的水平对齐方式。
align-content:
控制网格内容在容器内的垂直对齐方式。
place-content:
justify-content 和 align-content 的简写形式。
justify-content 和 align-content 这两个属性值相同,都可以取下面这些值。
.container {
justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
align-content: start | end | center | stretch | space-around | space-between | space-evenly;
}
-
start:对齐到容器的起始位置。。
-
end:对齐到容器的结束位置。
-
center:容器内部居中。
-
stretch:项目大小没有指定时,拉伸占据整个网格容器。
-
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与容器边框的间隔大一倍。
-
space-between:项目与项目的间隔相等,项目与容器边框之间没有间隔。
-
space-evenly:项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。
也可使用它们的合并简写形式。
place-content: <align-content> <justify-content>
- start:对齐到容器的起始位置。
- end:对齐到容器的结束位置。
- center:容器内部居中。
- stretch:项目大小没有指定时,拉伸占据整个网格容器。
- space-around:每个项目两侧的间隔相等。
- space-between:项目与项目的间隔相等,项目与容器边框之间没有间隔。
- space-evenly:项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。
grid-auto-columns && grid-auto-rows
grid-auto-columns 和 grid-auto-rows 定义了当网格项超出显式网格范围时,浏览器自动创建的列和行的大小,即隐式网格的大小。
-
grid-auto-columns
:定义隐式网格列的宽度。 -
grid-auto-rows
:定义隐式网格行的高度。
.container {
display: grid;
grid-template-columns: 100px 100px 100px 100px; /* 显式定义 4 列 */
grid-template-rows: 100px 100px; /* 显式定义 2 行 */
grid-auto-rows: 200px; /* 隐式网格行高为 200px */
grid-gap: 10px;
}
上述代码,显示定义了4列和2行 ,但是有9个元素,就会产生隐式网格。通过 grid-auto-rows 可以指定隐式网格的行高为 200px。
grid-template-areas 属性
grid-template-areas 是 Grid 布局中用于定义网格区域的属性。它通过命名区域的方式,直观地描述网格布局的结构。
该属性一般和项目属性中的 grid-area 属性一起使用,因此会在下面详细解释。
grid-template && grid
grid-template 属性和 grid 属性是 Grid 布局中的简写属性,用于一次性定义网格的多个属性:
- grid-template属性:grid-template-rows、grid-template-columns 和 grid-template-areas 这三个属性的合并简写形式。
- grid属性:grid-template-rows、grid-template-columns、grid-template-areas、 grid-auto-rows、grid-auto-columns、grid-auto-flow 这六个属性的合并简写形式。
这种简写语法虽然方便,但会降低代码的可读性和可维护性,不推荐使用。
项目属性
项目属性是指定义在项目上的属性,用于控制每个网格项的具体位置和表现。
grid-column && grid-row
项目的位置是可以指定的,具体方法就是指定项目的四个边框,分别定位在哪根网格线。上面说过9宫格共有4根水平网格线和4根垂直网格线,通过指定这些网格线的起始和结束位置,来定义网格项占据的列和行范围。
下面这些属性用于控制项目在容器中的位置。
grid-column-start属性:网格项在列方向的起始网格线。
grid-column-end属性:网格项在列方向的结束网格线。
grid-row-start属性:网格项在行方向的起始网格线。
grid-row-end属性:网格项在行方向的结束网格线。
.item-1 {
grid-column-start: 1; /* 从第 1 列开始 */
grid-column-end: 3; /* 到第 3 列结束 */
grid-row-start: 2; /* 从第 2 行开始 */
grid-row-end: 4; /* 到第 4 行结束 */
}
上面代码中,item-1
所在的网格项目,垂直网格线是从 1 到 3,水平网格线是从 2 到 4。
命名网格线
这四个属性的值,除了指定为第几个网格线,还可以指定为网格线的名字。
.item-1 {
grid-column-start: c1;
grid-column-end: c3;
}
命名网格线:grid-template-columns 属性和 grid-template-rows 属性里面,可以使用方括号,指定每一根网格线的名字。
.container {
display: grid;
grid-template-columns: [c1] 100px [c2] 100px [c3] 100px [c4];
grid-template-rows: [r1] 100px [r2] 100px [r3] 100px [r4];
}
上面代码指定网格布局为3行 x 3列,方括号里面依次是4根垂直网格线和4根水平网格线这八根线的名字。
span 关键字
这四个属性的值还可以使用 span 关键字,表示跨越的行数(列数),即左右边框(上下边框)之间跨越多少个网格。例如指定1号项目的左右边框跨越2个网格:
.item-1 {
grid-column-start: span 2;
}
合并简写形式
grid-column 属性是 grid-column-start 和 grid-column-end 的合并简写形式,grid-row 属性是 grid-row-start 属性和 grid-row-end 的合并简写形式。
.item {
grid-column: <start-line> / <end-line>;
grid-row: <start-line> / <end-line>;
}
下面代码中的几种写法都是等效的。
.item-1 {
grid-column: 1 / 3;
grid-row: 1 / 2;
}
/* 等同于 */
.item-1 {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 2;
}
/* 等同于 */
.item-1 {
background: #b03532;
grid-column: 1 / span 2;
grid-row: 1;
}
斜杠以及后面的部分可以省略,默认跨越一个网格。
justify-self && align-self && place-self
这些属性用于设置单元格内的对齐方式。
justify-self:
设置单元格内的水平对齐方式。
align-self:
设置单元格内的垂直对齐方式。
place-self:
justify-self 和 align-self 的简写形式。
justify-self 和 align-self 这两个属性值相同,都可以取下面这些值。
.item {
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
}
这三个属性跟 justify-items 、align-items 和 place-items 的用法一致,区别是只作用于单个项目。
grid-area 属性
grid-area 属性通常搭配项目属性 grid-template-areas 使用。
-
grid-template-areas
:通过命名区域的方式,定义网格区域的布局结构。 -
grid-area
:将项目分配到指定的区域。
下面这张图片,展示了我们在网站建设中最常见的布局。
如果不使用 grid-area 的话,我们通常会这样实现:
.grid {
display: grid;
grid-template-columns: 120px 1fr;
grid-template-rows: 80px 1fr;
}
.sidebar {
grid-column: 1;
grid-row: 2 / 3;
}
.header {
grid-column: 2;
grid-row: 1;
}
.content {
grid-column: 2;
grid-row: 2;
}
这种实现方式的可读性不够强,而 grid-template-areas 网格区域通过命名区域的方式,能够更直观地描述网格布局的结构,提高代码的可读性和可维护性。
.container {
display: grid;
grid-template-columns: 120px 1fr;
grid-template-rows: 80px 1fr;
grid-template-areas:
". header"
"sidebar content";
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.content {
grid-area: content;
}
上面代码中,顶部是页眉区域 header,中间部分则为 siderbar 和 content。其中点(.
)表示没有用到该单元格,或者该单元格不属于任何区域。
给每块网格划分区域并且命名后,我们不是给子节点分配 grid-column 和 grid-row,而是给它分配 grid-area。效果:
当希望某个特定区域跨越多行或多列时,可以在模板中重复该区域的名称。
grid-template-areas:
"....... header header"
"sidebar content content";
结语
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~