JavaScript五十问——对比来说CSS的Grid与FlexBox(下篇)
前言
在上篇——JavaScript五十问——对比来说CSS的Grid与FlexBox(上篇),我介绍了Flex的属性与使用,今天我们来总结一下Grid的具体使用方法,最后会结合Flex与Grid布局讲一讲二者的联系与不同。
需要注意得是,Grid布局与我们之前所熟悉的css布局思路有很大的不同,阅读这篇文章之前,需要把我们平时对css的刻板印象抛弃掉,准备接受知识的洗礼吧,少年!
Grid
与 Flex 相同,Grid 也分为容器与元素两个概念;在一个 html 标签中添加样式:display:grid
或者display:inline-grid
,即构建了一个 Grid 的容器,里面的 dom 元素即为 Grid 元素。同样,Grid 也分为两类属性,分别装载在容器与元素上,下面一一说明。
HTML结构说明
以下所有例子均基于或扩展于下面的HTML结构:
<style> .container{ width:500px; background-color:#999; } .item{ width: 50px; background-color:#567; color:#fff; } </style> <div class="container"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> <div class="item">4</div> <div class="item">5</div> </div>
Grid 基本概念介绍
网格
Grid容器里面有网格一系列的概念;听着唬人,但是结合图很容易理解。
网格线
水平方向有垂直方向的线段即为网格线
网格轨道
两个相邻的平行网格线之间的区域就是网格轨道
网格单元
四个相邻边组成的区域就是网格单元。
fr
fr
是Grid中特有表示尺寸
的单位,是分数——fraction
的缩写,假设我们现在有四个grid元素,每个元素的宽度都是1fr,那么每个元素的实际宽度就是总宽度的1/4。
fr也可以跟%,px 共同使用,他的计算规则就是刨去px与%的剩余空间作为fr分配空间,所有fr相加之和作为分母,分子为每个元素对应的fr的值。(当然,在Grid语境下,我们是不需要设置width属性的,这样说是为了让大家容易理解)。
这样说来好像fr仅仅是%的另外一种写法,随着我的介绍,你就会发现fr优于%的地方。
接下来,我还是以脑图为思路介绍Grid的各个属性。
容器属性
grid-template
grid-tempalte是三个属性的简写,这三个属性都是描述整块区域即多个网格单元
的属性。
grid-template-rows
grid-template-rows是描述横向
的单元轨道属性
的。可以试想一下,我们在一个Grid容器中,关于这个属性,我们关心的是什么呢?无非就是这个容器中有多少行,每行的高度;所以,这个属性就是让我们定义这些值得。先来看语法:grid-template-rows: <track-size> | <line-name> <track-size> ...;
这个属性除了可以定义轨道尺寸和个数之外,还允许我们定义两个轨道之间的网格线的名称,至于他的作用,我们先按下不表,先来看这个属性是怎样定义每个轨道的尺寸和轨道个数的。
先来看一个例子:
.container{ grid-template-rows:200px auto 1fr 1fr 20%; grid-row-gap:10px;//定义轨道之间的间距 }
grid-template-rows定义了五个值,表示Grid容器里面有五行,可以使用任意的定义尺寸的方式,效果如下
其中auto
值就表示元素的实际占用大小。
Grid分配空间首先计算除了fr对应轨道的尺寸,然后将剩余尺寸按照比例分配给fr加持的元素。
以上,我们在Grid容器里定义了五行容器轨道,当我们定义轨道过多时,可以使用repeat函数来减少我们的工作量,语法为:grid-template-rows:repeat(n, size)
例子:
.container{ grid-template-rows:repeat(5,1fr); grid-row-gap:10px; }
上面就定义了五个网格轨道,每个轨道的高度是Grid容器高度的1/5。
grid-template-columns
grid-template-columns 与 grid-template-rows使用方法是一致的,这两个属性共同作用于Grid容器,相当于把Grid容器分割为m*n个子区域。
例子:
.container{ grid-template-rows:repeat(3,1fr); grid-template-columns: repeat(2, 1fr)
上面这个例子就会得到六个均等分的子区域。
通过上面两个属性,相信大家对Grid布局有一个基本的认识了,想必对Grid二维布局的模式也有一些概念了,接下来才是Grid精彩之处,震撼灵魂的地方!
grid-template-areas
上面两个属性分别设置Grid行属性和列属性,grid-template-areas是设置Grid区域的。所谓区域是由一个或多个行、列、单元组成的一篇区域。首先看一下语法:
.container { grid-template-areas: "<grid-area-name> | . | none | ..." "..."; }
其中grid-area-name
表示网格区域的名称.
表示空的网格区域none
表示没有定义网格区域
在我们平时开发时,经常会出现上头下尾中间两栏布局的情况,下面我们使用grid-template-areas完成这样的布局。
<style> .container{ display:grid; grid-template-rows:60px auto 60px; grid-template-columns:100px 1fr; grid-template-areas: "header header" "left right" "footer footer"; } .container .item:first-child{ grid-area: header; } .container .item:nth-child(2){ grid-area: left; } .container .item:nth-child(3){ grid-area: right; } .container .item:nth-child(4){ grid-area: footer; } </style> .container{ display:grid; grid-template-rows:60px 1fr 60px; grid-template-columns:100px 1fr; grid-template-areas: "header header" "left right" "footer footer"; } .container .item:first-child{ grid-area: header; } .container .item:nth-child(2){ grid-area: left; } .container .item:nth-child(3){ grid-area: right; } .container .item:nth-child(4){ grid-area: footer; } .container{ display:grid; grid-template-rows:60px 1fr 60px; grid-template-columns:100px 1fr; grid-template-areas: "header header" "left right" "footer footer"; } .container .item:first-child{ grid-area: header; } .container .item:nth-child(2){ grid-area: left; } .container .item:nth-child(3){ grid-area: right; } .container .item:nth-child(4){ grid-area: footer; } </style> <div class="container"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> <div class="item">4</div> </div>
在Grid容器中,我们定义了6个网格单元,使用grid-tempalte-areas
划分了header footer left right 四片区域;而在grid元素
中,每个元素使用grid-area
来指定元素所对应的grid区域。因此,我们虽然划分了6个单元,但可以使用四个元素来表示。
是不是很神奇呢,更神奇的是,grid-area-name是支持中文定义的。
以上grid-template的子属性就说完了,grid-template是以上那三个属性的简写方式,语法如下:
grid-tempalte:<'grid-template-rows'> / <'grid-template-columns'>` `grid-tempalte:[ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?
例如上面的例子可以这样简写
grid-template: "header header" 60px "left right" 1fr "footer footer" 60px / 50px 1fr;
grid-gap
grid-gap用来描述Grid 区域之间间隙的尺寸大小。语法如下:
.container { grid-gap: <grid-row-gap> <grid-column-gap>; }
grid-gap是简写属性,也可以分别定义grid行间隔和grid 列间隔。
.container{ grid-row-gap:10px; grid-column-gap:10px; }
grid-gap与margin与padding不同,它不占用当前元素的盒模型的位置。
上图显示的很清楚,3号元素的margin 与 padding 均为零。
place-items
place-items
是 justify-items
和align-items
的简写方式
这两个属性分别定义了grid元素
水平与垂直分布方式。
语法如下:
justify-items: stretch | start | end | center; align-items: stretch | start | end | center;
对于这四个属性,默认stretch,相信读者在熟悉了Flex布局后都不会陌生,这里不多做解释,直接看例子,以align-items 为例:
.container{ display:grid; grid-template: "header header" 160px "footer footer" 160px / 160px 160px; height:500px; grid-row-gap:10px; grid-column-gap: 10px; }
首先定义四个grid单元,每个单元的长宽均为160px
接下来我们更改align-items的值
align-items:stretch
align-items:center
align-items:start
align-items: end
为了方便大家理解,我用红框框出每个Grid单元所占用的空间。由此可以看出,place-items属性是用来表明一个元素在当前grid单元中的分布方式,这个元素的拉伸,居中等都是以grid单元作为参考的。
place-content
place-content同样是一个简写属性,它包括:justify-content 和 align-content,它表示grid元素在grid容器中的分布方式,只有当grid容器中有剩余空间的时候才起作用。
语法如下:
justify-content: stretch | start | end | center | space-between | space-around | space-evenly; align-content: stretch | start | end | center | space-between | space-around | space-evenly;
属性值得含义同Flex;这里不再过多说明,读者可以自行验证。
grid-auto-rows 与 grid-auto-columns
grid-auto-rows 和 grid-auto-columns;用于当实际的Grid的元素多余划分的Grid元素时,定义多余Grid元素的长宽;
例如我们在HTML里面一定了五个Grid元素,但是在css中只定义了2*2的Grid单元,可以使用grid-auto来定义多出来的轨道的尺寸。
grid-auto-flow
grid-auto-flow的用法需要结合下面的元素属性来说明。
元素属性
grid-column-start grid-column-end
两个属性是用来定义Grid元素列方向上的起始与终止位置。
语法格式为:
grid-column-start: <number> | <name> | span <number> | span <name> | auto
其中:
number为起止第几条网格线
name 为网格线的名称
span <number>网格元素会跨越网格单元的数量
span <name> 当前的网格元素会在哪一个网格线上开始or终止
注意 使用span 如果是start,表示这个从开始的位置跨过的grid单元,如果是end 表示这个元素覆盖的grid单元。
grid-column是它们的简写方式,语法为:
grid-column:grid-column-start / grid-column-end
grid-row-start grid-row-end
grid-row属性与grid-column用法一致,这里不过多赘言,直接看例子:
.container{ display:grid; grid-template-rows:[rone]1fr[rtwo]3fr[rthree]1fr[rfour]; grid-template-columns: [cone]1fr[ctwo]5fr[cthree]2fr[cfour]; height:500px; } .item:first-child{ grid-column-start:1; grid-column-end:cfour; grid-row-start:rone; grid-row-end: 2; } .item:nth-child(2){ grid-column-start:1; grid-column-end:span 1; grid-row-start:rtwo; grid-row-end: span cthree; } .item:nth-child(3){ grid-column-start: ctwo; grid-column-end:4; grid-row-start:rtwo; grid-row-end: span cthree; } .item:nth-child(4){ grid-column-start:1; grid-column-end:4; grid-row-start:3; grid-row-end: span 4; }
效果:
首先在Grid容器中划分出9个grid单元,这九个单元被六个网格线所分割,并给这六个网格线命名。在四个Grid元素中定义横行的起始位置。
grid-row 与 grid-column结合使用,可以起到与Grid-template-areas同样的效果。
grid-area
grid-area我们在前面已经接触过一部分了,他与Grid容器中的grid-template-areas一起定义,也是grid-column与grid-row的简写属性,语法为:
grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
name为grid区域的名称,与grid-tempalte-areas结合使用。
上面我们的例子就可以用grid-area表示:
.item:first-child{ grid-area:1/rone/2/cfour; }
place-self
justify-self
justify-self定义Grid元素的水平布局方式的,例如,我们在Grid容器中定义justify-items的属性为默认属性,而在某一个Grid元素中定义justify-self为center,那么其他元素表现为拉伸,这个元素则单独表现为居中。也就是说,justify-self在Grid容器中对应的属性是justify-items。
语法为:
.item { justify-self: stretch | start | end | center; }
algin-self
align-self与justify-self一致,改变的是这个元素的垂直部署方式,与容器中align-items对应,
语法为:
.container { align-self: stretch | start | end | center; }
由于这四个属性值已经在我们的系列文章中出现多次,这里不再多说。
plac-self是以上两个属性的简写方式,语法为:
.container { place-items: <align-self> / <justify-self>; }
再论fr
Grid的所有属性已经介绍完毕了,在对Grid宇宙有了一个基本的认识后,我们再回头看一下Grid宇宙中出现的新尺寸单位——fr。
可能大家在刚刚结仇到fr这个单位时,都会认为它是%的一个别名;但是,我们来看最下面的例子:
我们在Grid容器中划分出四个Grid区域,并定义每个区域的宽度为25%,并定义每个Grid元素之间的gap宽70px;
.container{ display:grid; grid-template-columns: repeat(4, 25%); grid-column-gap: 70px; }
效果如下:
很明显,这里元素溢出了。这种情况是我们不想看到的。
下面,我们将25%替换为1fr看一下效果:
效果对比很明显。
而造成两者显著区别的原因是二者的计算空间方式的不同。使用百分比它的分母是父元素的width或者height,而fr的分母是父元素中剩余空间的尺寸;css会首先计算使用%和px定义的元素尺寸,剩下的空间再由fr元素进行比例分配。这就是使用fr不会出现元素溢出的情况。当然我们也可以使用calc避免溢出的尴尬,但是两种解决方案孰优孰略已经一目了然了。
Flex 与 Grid
Flex布局与Grid布局有很多相似的地方,例如justify-content和justify-items的用法。但是更多的是不同,最重要的是Grid开拓了二维
布局的方式,相比于传统的css布局方式(Flex、bootstrp 12列),Grid开创了网格的概念,用户可以从横纵
两个方面部署元素。正是因为如此,在Grid宇宙中,传统的css布局、尺寸属性就显得格格不入了。而Grid布局的二维特性所带来的整体观,使Grid在页面级别
样式上更加游刃有余。而Flex相比于Grid 更加适合小组件上的样式开发,二者并不冲突相信在Grid 与 Flex双双加持之下,一定会收获更好的开发效果!
Grid布局还是一个较新潮的概念,我也是一般看资料学习,一边分享,由于缺少实际的开发经验,对于很多属性的应用场景还没有很深入的理解,故而有的属性一笔带过;如果我有理解不正确的地方,欢迎大家指正!
参考文献
MDN:Grid Layout
张鑫旭:写给自己看的display: grid布局教程
知乎:CSS 新的长度单位 fr 你知道么?
What's more:
JavaScript五十问——对比来说CSS的Grid与FlexBox(上篇)