likes
comments
collection
share

css 中最强大的布局方式 -- Grid 布局 (上)

作者站长头像
站长
· 阅读数 14

引言--面试题

没错,这一次又是由一道面试题引发的思考, 相信这道经典的道题大家刷题的时候也没少见到过。

面试官:菜单左中右布局,两边定宽,中间自适应,说一下有几种实现方式。

而我只知道 flex 布局、 float 浮动,好像还有个 position也可以实现

css 中最强大的布局方式 -- Grid 布局 (上)

于是带着这个面试题我找到了万能的 chatGPT 🤣, 看看他能给出什么样的答复!

chatGPT 给出了三种实现方式,根据他的回复我做出了相应实现:

  1. 使用Flex布局:将父容器设置为 flex 布局,左右两个元素设置固定宽度,中间的自适应元素设置为flex:1,即可实现中间自适应,左右固定宽度的效果。
<!-- 使用 flex 布局实现左中右三栏 -->
<p class="title"> flex 布局实现左中右三栏: </p>
<div class="container flex-container">
  <div class="flex-left">左侧固定宽度</div>
  <div class="flex-middle">中间自适应宽度</div>
  <div class="flex-right">右侧固定宽度</div>
</div>

<!-- flex 布局实现样式 -->

.title {
  margin-top: 50px;
}
.container {
  height: 80px;
  line-height: 80px;
}
/* 使用flex布局实现左中右三栏 */
.flex-container {
  display: flex;
  text-align: center;

}
.flex-left {
  width: 200px;
  background-color: #c9eccb;

}
.flex-middle {
  flex: 1;
  background-color: #c7e7e9;
  margin:0 20px;


}
.flex-right {
  width: 200px;
  background-color: #b2ede2;

}


  1. 使用浮动布局:将左右两个固定宽度的元素分别浮动到左右两侧,使用 margin 将中间的自适应元素向内缩进,以避免与左右元素重叠。
<!-- 使用 float 布局实现左中右三栏 -->
<p class="title"> float 布局实现左中右三栏:</p>
<div class="container float-container">
  <div class="float-left">左侧固定宽度</div>
  <div class="float-right">右侧固定宽度</div>
  <div class="float-middle">中间自适应宽度</div>
</div>


/* float 布局实现样式 */
.float-container {
  overflow: hidden;
  margin:20px 0;
  text-align: center;

}
.float-left {
  float: left;
  width: 200px;
  background-color: #c9eccb;

}

.float-middle {
  margin: 0 220px;
  background-color: #c7e7e9;

}    
.float-right {
  float: right;
  width: 200px;
  background-color: #b2ede2;

}

同时在使用 float 属性时要注意高度塌陷问题:

当一个元素被设置为 float 时,它会从正常的文档流中移除,并且周围的元素会重新排列以适应它的位置。这可能会导致包含该元素的父元素无法正确计算其高度,从而导致高度塌陷的问题。

为了解决这个问题,可以使用一些技巧,如在父元素中添加一个空的div,并使用 clear 属性来清除 float 元素的影响,或者使用 flex 布局或 grid 布局等方式来代替 float。

在这里还要再补充一个 position 的实现,position 与 float 类似,将两个侧边栏定位在左右两边。使得中间的部分能够自适应。

 <!-- 使用 position 布局实现左中右三栏 -->
  <p class="title"> position 布局实现左中右三栏:</p>
  <div class="container position-container">
    <div class="position-left">左侧固定宽度</div>
    <div class="position-middle">中间自适应宽度</div>
    <div class="position-right">右侧固定宽度</div>
  </div>

 /* position 实现样式*/
 .position-container {
  position: relative;
  text-align: center;

}
.position-left {
  width: 200px;
  position: absolute;
  left: 0;
  top:0;
  background-color: #c9eccb;

}

.position-middle {
  margin: 0 220px;
  background-color: #c7e7e9;

}    
.position-right {
  width: 200px;
  position: absolute;
  right: 0;
  top:0;
  background-color: #b2ede2;
}
  1. 使用 Grid布局:将父容器设置为 Grid 布局,设置三列,左右两个固定宽度的元素分别放在第一列和第三列,中间自适应元素放在第二列,即可实现该布局效果。

由于平时些页面的时候 Grid 布局写的少😂,所以看到这个方法时一头雾水,一时间没有实现,所以带着对 Grid 的好奇开始了新的学习和思考。

  • 到底什么是 Grid 布局呢?
  • 它与 flex 有什么区别吗?
  • 有一个 flex 布局不就够了吗,为什么还要来一个 Grid 布局?

初探 Grid 布局

Grid 布局是一种用于网页布局的 CSS 技术,它能够将页面分割成行和列的网格,在这些网格中放置相应的内容。使用 Grid 布局可以更轻松地创建复杂的布局,且无需使用嵌套的 div 元素或其他技巧。 Grid 布局号称是最强大的的 CSS 布局方案,可以更好地管理和组织页面上的元素。

为什么有了 flex 还要来一个 Grid 呢? 在布局的时候,我们脑海中第一个蹦出来的肯定就是 flex ,但是跟 flex 相比 Grid 又有什么不一样的呢:

  • flex 是一维布局,一次只能处理一个维度上的元素布局,一行或者一列,支持对齐和分布
  • Grid 是二维布局,将容器划分成了行和列的网格,并能够精确地控制每个网格的大小和位置,更适合创建复杂的布局

css 中最强大的布局方式 -- Grid 布局 (上) 相较而言 Grid 布局远比 flex 更加强大!

Grid 的基础概念

先来看看 grid 的基本实现:

<div class="container">
  <div class="item one">Item one</div>
  <div class="item two">Item two</div>
  <div class="item third">Item third</div>
  <div class="item one">Item four</div>
  <div class="item two">Item five</div>
  <div class="item third">Item six</div>
</div>


<style>
  .container {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;  // 网格容器将被分成三个相等的列
    grid-template-rows: auto;  // 行
  }
  .item {
    margin: 20px;
    padding: 20px;
    height: 80px;
    line-height: 80px;
  }
  .one {
    background-color: #c9eccb;
  }
  .two {
    background-color: #c7e7e9;
  }
  .third {
    background-color: #b2ede2;
  }
</style>
  1. 网格容器: 通过声明 display:griddisplay:inline-grid 创建网格容器。容器内的所有子元素将成为网格项目。
    • display:grid,表示该容器是一个块级元素
    • display:inline-grid,表示该容器元素为行内元素
  2. 网格线:划分网格的线,称为"网格线"

注意:当我们定义网格时,我们定义的是网格轨道,而不是网格线。

m 列有 m + 1 根垂直的网格线,n 行有 n + 1 跟水平网格线。 并且 Grid 会为我们创建网格线的编号来方便定位每一个网格元素,这些编号有正向的排序当然也会有逆序。 css 中最强大的布局方式 -- Grid 布局 (上)

  1. 网格轨道:网格轨道可以是水平轨道或垂直轨道,它们是由网格线定义的。

在 Grid 布局中,使用 grid-template-columns 和 grid-template-rows 来定义网格轨道的大小和数量。 css 中最强大的布局方式 -- Grid 布局 (上)

  1. 网格单元:网格单元是在一个网格元素中最小的单位,由网格线围成,定义了网格单元的边界。

css 中最强大的布局方式 -- Grid 布局 (上)

grid 的属性

grid-template-columns - 网格轨道

grid-template-columns 和 grid-template-rows 来定义网格轨道的大小和数量

  • grid-template-columns:用于定义网格布局中每一个轨道的列宽,后面有几个值,表示多少列,每个值为该列的列宽
  • grid-template-rows:用于定义网格布局中每一个轨道的行高,后面有几个值,表示有多少行,每个值为该行的行高

轨道大小可以使用任意 css 长度、百分比、或者分数、或者 fr 单位 在上面的案例中我们就用到了 fr 相对单位

fr 相对单位

fr 表示网格容器中可用空间的占比,当然也可以使用 px 来给定表格的固定宽高。

  • grid-template-columns: 1fr 1fr 1fr 将网格容器划分成三个相等的列,
  • grid-template-columns: 100px 1fr 2fr; 将创建三个网格轨道,第一个的宽度为 100px,第二个为网格容器中可用空间的 1/3,第三个轨道的宽度为网格可用空间的 2/3。

fr 可以更加灵活地控制网格容器中各个列的宽度。

auto & auto-filll

auto-fill 和 auto 关键字都可以用于指定网格轨道的数量。 它们之间的区别在于,当网格容器中的项目不足以填充所有的网格轨道时,是否会创建多余的网格轨道。

auto-fill

  • 会自动创建多余的网格轨道以填充可用空间。
  • grid-template-column: repeat(auto-fill, 200px) 表示列宽是 200px,但列的数量不固定,只要浏览器能够容纳得下,就可以放置元素。

auto:

  • 能够自动填充可用空间,但不会创建多余的网格轨道。
  • 通过 auto 关键字,我们可以实现两列或者三列布局,grid-template-columns: 100px auto 100px

repeat() 函数

在 Grid 布局中可以 repeat() 函数来简化定义网格轨道的过程,避免重复。

grid-template-columns: repeat(3, 100px);

这个函数接受两个参数:

  • 第一个参数指定要重复的次数,
  • 第二个参数指定每个重复项的大小。

repeat() 函数还可以用于指定不同大小的网格轨道。

例如,grid-template-columns: repeat(2, 100px) repeat(3, 1fr) repeat(4, 2fr); 先创建两个宽度为 100px的列,接着是三个占据可用空间的列,最后是四个宽度为可用空间的 2/3 的列。

minmax() 函数

minmax() 函数用于指定网格轨道的大小范围。 接受两个参数:分别为网格轨道的最小值和最大值

.container {
  display: grid;
  grid-template-columns: repeat(2, minmax(200px, 1fr));
}

minmax() 通常与 repeat() 一起使用,以指定重复的网格轨道的大小范围。

minmax() 使 Grid 布局更加灵活,因为它允许指定网格轨道的大小范围,并且支持在不同的屏幕尺寸和布局下自动调整,实现页面的响应式。

网格线名称

grid-template-columns、grid-template-rows 属性中,还可以使用方括号 [] ,指定每一根网格线的名字,以便后续的引用。

<div class="container">
  <div class="item">Item 1</div>
  <div class="item">Item 2</div>
  <div class="item">Item 3</div>
  <div class="item">Item 4</div>
  <div class="item">Item 5</div>
  <div class="item">Item 6</div>
</div>


.container {
  display: grid;
  grid-template-columns: [col1] 100px [col2] 1fr [col3] ; // 设置了三条纵向网格线的名称及轨道的列宽
  grid-template-rows: [row1] 50px [row2] 1fr [row3] ;   // 设置了三条横向网格线的名称及轨道的行高
}
.item {
  padding: 20px;
  height: 80px;
  line-height: 80px;
  margin: 20px;
   background-color: #caedbb;
  
}

此时为三行两列的 Grid 布局, grid-template-columns:[col1] 100px [col2] 1fr [col3] 为两列是因为设置了最后一条网格线名称,但是并没有赋值轨道大小。 css 中最强大的布局方式 -- Grid 布局 (上) 在设置了网格线名称以及轨道的大小后可以通过名称来指定网格单元的位置

.item:nth-child(1) {
  grid-column-start: col1;
  grid-column-end: col3;
  grid-row-start: row1;
  grid-row-end: row2;
  background-color: #c7e7e9;
}

通过网格线的名称确定了第一个元素的范围,而后续的元素依旧按照原来的顺序依次排序。 css 中最强大的布局方式 -- Grid 布局 (上)

grid-gap - 网格间距

grid-gap 是 grid-row-gap 、grid-column-gap 属性的简写形式

  • grid-row-gap:行间距,缩写为 row-gap,row-gap 是最新写法,但原来的 grid- 写法仍然保留
  • grid-column-gap: 列间距,缩写为 column-gap
  • grid-gap: 为 行间距、列间距的缩写
    • 一个值时同时设置行列间距
    • 两个值时分别表示行间距、列间距
    • 缩写为 gap

grid-auto-flow - 网格方向

grid-auto-flow 指定了 Grid 容器的布局方式,该属性有两个可能的值:row 和 column。默认为 row 采用先行后列的方式进行排布。

  • grid-auto-flow: row:表示先行后列进行排布
  • grid-auto-flow: column:表示先列后行进行排布
  • grid-auto-flow: row-dense:在先行后列排布的基础下,项目自动向前补位尽量填满行,不留空格
  • grid-auto-flow: column-dense:表示在先列后行排布的基础下,尽量填满列,且不留空格

例如 设置 grid-auto-flow: row dense 后 ,图中的 Seven 自动向前补位置所在的空余位置。 css 中最强大的布局方式 -- Grid 布局 (上)

grid-template-areas - 单元格区域

grid-template-areas 属性使用区域名称来指定 Grid 布局中的项目位置,在网格容器中使用

  • 一个区域可以包括多个单元格。
  • 使用引号包裹的字符串表示一行网格轨道,可以为区域内的单元格指定区域名称
  • .标识空的网格单元
  • 未定义的网格区域用 none 标识
  • 有上下、左右相同的area相邻,表示其单元格合并,但必须是规则的排布,如L、T字形的就不行

grid-template-areas 经常与 grid-area 搭配使用

grid-area 属性指定 Grid 布局中项目放置的位置和范围,应用于网格项目中

例如 grid-area: 1 / 1 / 3 / 3 表示该项目的范围从(1/1)第一行第一列开始,结束于(3/3)第三行第三列。

在网格容器 container 中指定了各网格区域对应的名称,并在相应的类名中通过 grid-area 指定了项目对应的所在的区域。

.container {
  display: grid;
  grid-template-columns: [col1] 200px [col2] 1fr [col3] 1fr;
  grid-template-rows: [row1] 1fr [row2] 1fr [row3] 1fr;
  grid-gap:10px 20px;
  grid-template-areas:
    '. one two'
    'third . four';
}
.item {
  padding: 20px;
  height: 80px;
  line-height: 80px;
 	background-color: #caedbb;

}
.one {
  grid-area: one
}
.two {
  grid-area: two
}
.third {
  grid-area: third
}
.four {
  grid-area: four
}

这里特地将 Item 5 的类名设置为 four,是为了观察 . 网格单元在不指定网格名称的情况下是如何排列的。

<div class="container">
  <div class="item one">Item 1</div>
  <div class="item two">Item 2</div>
  <div class="item third">Item 3</div>
  <div class="item ">Item 4</div>
  <div class="item four">Item 5</div>
  <div class="item">Item 6</div>
</div>

根据效果我们能发现 .的位置由未指定名称的项目自动向前补位。 css 中最强大的布局方式 -- Grid 布局 (上)

小结

相信看到这里的同学对 Grid 布局也有了一定的了解,那么面试题中的使用 Grid 布局的写法也一定能有所实现了,在本文的结尾揭晓一下我的写法:

<!-- 使用 grid 布局实现左中右三栏 -->
<p class="title"> grid 布局实现左中右三栏:</p>
<div class="container grid-container">
  <div class="grid-left">左侧固定宽度</div>
  <div class="grid-middle">中间自适应宽度</div>
  <div class="grid-right">右侧固定宽度</div>
</div>	

/* grid 布局实现样式 */
.grid-container {
  display: grid;
  grid-template-columns: 200px auto 200px;
  grid-template-rows: auto;
  text-align: center;
}

.grid-left {
  grid-column-start: 1;
  grid-column-end: 2;
  background-color: #c9eccb;
}

.grid-middle {
  margin: 0 20px;
  grid-column-start: 2;
  grid-column-end: 3;
  background-color: #c7e7e9;
}

.grid-right {
  grid-column-start: 3;
  grid-column-end: 4;
  background-color: #b2ede2;
}

实现了中间栏自适应的效果

关于 Grid 布局的相关知识先介绍到这里,后续将继续学习单元格内的属性设置,敬请期待!!!

参考

  1. 一文搞懂Grid 布局
  2. 【CSS】Grid布局-系列