简单dom结构,实现复杂的布局--Grid

580 阅读12分钟

grid布局能够以最简单的DOM结构实现复杂的布局,也可以让我们不必改变结构的情况下更灵活的扩展布局,相对于flex更加灵活,非常强大,现在让我们一起学习下吧。

什么是grid?

grid即网格,是一组相交的水平线和垂直线,它定义了网格的列和行。我们可以将网格元素放置在与这些行和列相关的位置上。CSS 网格布局具有固定的位置和弹性的轨道大小,创建轨道大小可以是固定的像素单位,也可以是弹性的,比如百分比或专门的轨道尺寸单位fr来创建。

fr单位:代表网格容器中可用空间的一等份,会随着可用空间增长和收缩。

grid兼容性

grid兼容性如下图 image.png

在最新版本的Chrome、Firefox和Edge中,CSS Grid已经完全实现。然而,在使用其他浏览器,比如Safari和IE时,就要小心了。Safari 10.1 +、iOS Safari 10.3 + 和 IE 11 及更早的版本不支持CSS Grid。

那么如果使用grid该如何处理其兼容性呢?以下有两个方案供大家参考:

1、flexbox回退方案

可以考虑使用 Flexbox 做为一个回退方案,一旦你在一个使用 flex 属性上使用 grid 布局,flex就会失效。同时引入Autoprefixer插件。Autoprefixer能够自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀,能够处理flexbox在不同浏览器的flexbugs

举个栗子:

.container {
    /* Grid 属性 */
    display: grid;
    grid-gap: 10px;
    grid-template-columns: repeat(4, 1fr);
    
    /* 不支持grid时的兼容处理 */
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: wrap;
    flex-wrap: wrap;
    -webkit-box-pack: justify;
    -ms-flex-pack: justify;
    justify-content: space-between;
}

2、CSS特效查询(Feature Queries)

Flexbox在不同的浏览器上,相同属性名相同属性值在页面中的表现也可能不一致。因此对于处理grid兼容性,也可以选择使用css特性查询。

css特性查询是使用css的@supports,@supports用于检测浏览器是否支持参数中的属性属性值,如果支持则渲染花括号中的css代码,类似于:

@supports (display: grid) {
   // code that will only run if CSS Grid is supported by the browser 
 }

注意:IE10、IE11是不支持@supports规则,所以压根不会进入这个条件判断,花括号中的css代码是不会渲染的。

举个例子:

  1. 首先是给旧浏览器做支持,准备一套Fallback method,保证在所有浏览器上都是工作的:
.wrapper{
  overflow:auto;
}
.item {
  float:left;
  width:33.3%;
}
  1. 再给支持Grid的浏览器做覆盖,覆盖代码分两部分,一部分是直接放入对旧浏览没有影响的:
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

因为旧浏览器不支持Grid布局,Grid相关属性旧浏览器都无法理解。在支持的浏览器中使得item由floated item转为grid item,这样的覆盖行为由css规定,更多覆盖规则感兴趣可以去了解一下。另一部分是直接放入对旧浏览器是有影响的,要做css特性查询:

@supports (display: grid) {
  .item {
      width: auto; // 覆盖原有的width:33.3%。
  }
}

没错,这里的实践得写两套样式。这时大家会想,这么麻烦,写一套支持所有浏览器的不就得了,干嘛非得用Grid?这是个很实际的问题,以下有几个场景建议使用Grid:

  1. 项目得支持IE10、IE11等旧浏览器,但是开发者想尝鲜Grid布局;
  2. 项目周期会很长,可能现在不支持Grid布局的浏览器,以后就支持了;
  3. 要实现的效果不使用Grid布局很难实现,且对在旧浏览器中访问效果要求不高,能看就行

容器属性

创建一个网格容器

我们通过在元素上声明 display:grid 或 display:inline-grid 来创建一个网格容器。这个元素的所有直系子元素将成为网格元素。

注意,设为网格布局以后,容器子元素(项目)的 float、display: inline-block、display: table-cell、vertical-align 和 column-* 等设置都将失效。但是 position 是不会失效的。

DOM结构

<div class="box"> 
   <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 class="item">6</div> 
   <div class="item">7</div> 
   <div class="item">8</div> 
   <div class="item">9</div> 
</div>

对应的css

  .box{
      display: grid;
  }
  .item{
     width:80px;
     height:80px;
  }
  .box .item:nth-of-type(2n) {
      background-color: green;
  }
  .box .item:nth-of-type(2n+1) {
      background-color: pink;
  }

image.png

此时子元素将默认单行排列默认情况下,当父元素设置grid, 默认grid-template-columns: 1fr,grid-template-rows:1fr

网格布局

网格布局主要由columsrows来定义列与行

  • 九宫格
.box {
    background: beige;
    display: grid;
    grid-template-columns: 33.3% 33.3% 33.3%;
    grid-template-rows: 200px 200px 200px;
}
.box .item:nth-of-type(2n) {
    background-color: pink;
}
.box .item:nth-of-type(2n+1) {
    background-color: LightBlue;
}

image.png

以上是一个宽度自适应,每个高度为固定200px的九宫格,自动换行从第4个元素开始。也就是grid-template-columns确定了3列,每一列占据整个屏幕的33.3%,而高度为固定高度200px

grid-template-columns: 1fr 1fr 1fr与之效果是一样的。

1fr是网格的轨道单位,会随着可用空间增长和收缩,假设你要设置一个2fr 1fr 1fr,那么第一个元素会是第二个元素的两倍

image.png

grid-template-columns & grid-template-rows

在以上例子中,我们使用grid-template-columns控制了网格列数,通过这个属性,我们可以不用单独对子元素进行控制,你希望得到几列,就设置几个值,可以是百分比,也可以是固定像素。使用grid-template-rows控制子元素的高度,同样可以是百分比,也可以是固定像素

 .box{
      background: beige;
      display: grid;
      grid-template-columns: 200px 100px 1fr 1fr;
      grid-template-rows: 200px 200px 200px 200px;
  }
  .box .item:nth-of-type(3n) {
      background-color: pink;
  }
  .box .item:nth-of-type(3n+1) {
      background-color: lightBlue;
  }
  .box .item:nth-of-type(3n+2) {
      background-color: green;
  }

image.png

grid-columns-gap

控制列之间的间距,间距使用的空间会在 使用弹性长度 fr 的轨道的空间计算前就被留出来

.box {
  background: beige;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 200px 200px 200px;
  grid-column-gap: 10px; /* 列之间10个像素*/
}
.box .item:nth-of-type(2n) {
    background-color: pink;
}
.box .item:nth-of-type(2n+1) {
    background-color: lightBlue;
}

image.png

grid-rows-gap

控制行之间的间距

.box{
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 200px 200px 200px;
    grid-row-gap: 10px; /* 行之间的间距 */
}

image.png

.box {
  display: grid;
  padding:10px;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 200px 200px 200px;
  grid-column-gap: 10px;
  grid-row-gap: 10px;
  padding: 10px;
}

image.png 给父元素添加padding: 10px;,然后分别设置grid-column-gap: 10px;,grid-row-gap:10就可以实现间距相等

也可以用column-gaprow-gap来替代grid-column-gapgrid-row-gap

gap的简写

gap:10px 20pxrow-gap:10pxcolumn-gap:20px的简写。如果省略了第二个值,浏览器认为第二个值等于第一个值。

注意:为了兼容浏览器,比如火狐 52-61,还需要使用带有前缀的属性grid-gap

repeat(repeat, values)

Repeat语句可以创建重复轨道,比如网格将有共计 10 个轨道,为 1 个1fr轨道后面跟着 1 个2fr轨道,该模式重复 5 次,就可以写成grid-template-columns: repeat(5, 1fr 2fr);

第一个值 repeat 可以是固定的数字 (n),表示重复 n 次,也可以是auto-fit或 auto-fill,表示自动填充,容纳不下的放到下一行。

在以上我们使用grid-template-columns:1fr 1fr 1fr的写法,也可以利用repeat来简写

.box{
  display: grid;
  /* grid-template-columns:1fr 1fr 1fr */
  grid-template-columns: repeat(3,1fr);
  /*grid-template-rows: 200px 200px 200px*/
  grid-template-rows: repeat(3, 200px);
  grid-column-gap: 10px;
  grid-row-gap: 10px;
  padding: 10px;
}

注意:auto-fit和auto-fill通常没有区别,只有在容器宽度大于子元素最小宽度总和时才有显示区别(配合minmax使用时) 比如: grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)); image.png

再看一下auto-fit: grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); image.png

根据上面例子,可以看到auto-fit 和 auto-fill都会以最小的宽度创建单元格,创建完成以后,如果有多余的单元格,auto-fit会把这些多余的单元格均分给每一网格项,再重新创建单元格,而auto-fill不会。

minmax(min, max)

  • 用来定义一个范围,最小值是min,最大值是max。
  • max可以设置为fr,min不可以。
  • 如果max值小于min,则该值会被视为min值
.box{
    display: grid;
    /* grid-template-columns: 1fr 1fr 1fr; */
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    grid-column-gap: 10px;
    grid-row-gap: 10px;
    padding: 10px;
 }

image.png

image.png

image.png 由上面可见:每个item最小200px,当缩小屏幕,item宽度逐渐压缩,当小于200px时,会逐行展示,当拉大屏幕,由于设置了auto-fit,item跟随屏幕自适应排列

grid-auto-columns & grid-auto-rows

指定自动生成的网格轨道的大小。

当定义的网格不够容纳所有网格项,多出的网格项另起一行排列,或者给某一个网格项指定到定义好的网格区域的外部,这个时候,浏览器会自动生成多余的网格,以便放置项目。

grid-auto-columns 和 grid-auto-rows 就可以用来定义这些自动生成的多余的网格的大小。

.box {
  display: grid;
   width: 500px;
   height: 400px;
   background: beige;
   grid-template-columns: 40px auto 50px 40px;
   grid-template-rows: 300px auto;
   grid-auto-rows: 50px;
   grid-gap: 5px;
}

image.png

如上面的代码所示,定义的是 2 行 4 列一共 8 个单元格,但有网格项10 个,所以最终生成的网格是 3 行 4 列,grid-auto-rows: 50px;让最后一行的行高为 50px。 grid-auto-columns 和 grid-auto-rows 的默认值为auto

grid-template-areas

命名网格区域名称

 * {
    padding: 0;
    margin: 0;
 }
.app {
  background: beige;
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
  grid-auto-rows: minmax(100px, auto);
  /** grid-template-areas给容器划分区域并命名**/
  grid-template-areas:
  "hd hd hd"
  "sd main main"
  "ft ft ft";
}
header {
  /**grid-area 属性指定项目放在哪一个区域。**/
  grid-area: hd;
  background-color: lightBlue;
}
aside {
  grid-area: sd;
  background-color: pink;
}
main {
  grid-area: main;
  background-color: yellow;
}
footer {
  grid-area: ft;
  background-color: palegreen;
}

image.png

如果某些区域不需要利用,则可以使用“点”(.)表示,例如:

grid-template-areas: "a b c" "d d f" "g . i";

justify-items & align-items

justify-items 定义了网格布局中项目的水平对齐方式,align-items 定义了竖直对齐方式。

  • start:内容与网格区域的左端对齐
  • end: 内容与网格区域的右端对齐
  • center: 内容位于网格区域的中间位置
  • stretch: 内容宽度占据整个网格区域空间(默认值)

以下展示justify-items不同取值效果,align-items类似 image.png

image.png

这个与 flex 布局的有些区别,flex 布局的对齐方式分主轴和交叉轴,主轴方向的改变影响属性作用的方向。而 grid 布局是二维的,行列方向是固定的,所以说到 justify 一定就是指行(水平)方向,align 就是指列(竖直)方向。

place-items

place-items 属性是 align-items 属性和 justify-items 属性的合并简写形式。 语法是 place-items: <align-items> <justify-items>; 如果省略第二个值,则浏览器认为与第一个值相等。

有了这个属性,水平垂直居中就很容易了

image.png

justify-content & align-content

justify-content 定义了网格容器中,网格区域在水平方向上的对齐方式,align-content 定义了网格区域在竖直方向上的对齐方式。

属性值比 items 要多:

  • start:网格区域与容器的左端对齐
  • end: 网格区域与容器的右端对齐
  • center: 网格区域位于容器的中间位置
  • stretch: 网格区域占据整个容器空间(默认值)
  • space-around:网格项之间设置等宽的间隙,与容器边框的间隙是项之间间隙的一半
  • space-between: 网格项之间设置等宽的间隙,与容器边框没有间隙
  • space-evenly:网格项之间和与容器边框设置等宽的间隙

image.png

image.png

justify-content:end举例 image.png justify-content:space-around举例 image.png

place-content

place-content 属性是 align-content 和 justify-content 的合并简写形式。语法是 place-content: <align-content> <justify-content>;

项目属性

grid-column-start & grid-column-end

控制列的跨度。 start:开始显示网格的地方, end:停止显示网格的地方,如果end=start,则认为占据第start列,与end=start+1效果一样

/**从列线1-列线2-列线3-列线4,跨越了3列轨道**/
.box .item:nth-of-type(1) {
    grid-column-start: 1;
    grid-column-end: 4; 
}

image.png 可以看到item1跨越了1-3列

grid-row-start & grid-row-end

控制行的跨度

/**从行线3-行线3,占据第三行轨道**/
  .box .item:nth-of-type(1) {
      grid-row-start: 3;
      grid-row-end: 3;
      grid-column-start: 1;
      grid-column-end: 3;
  }    

image.png 可以看到item1位于第三行,跨越1-3列

grid-column & grid-row

我们可以通过

grid-column:1/3来简grid-column-start:1;grid-column-end:3

grid-row:1/3来简写grid-row-start:1;grid-row-end:3

grid-area

grid-area是grid-row-startgrid-column-startgrid-row-endgrid-column-end的简写

.box .item:nth-of-type(2) {
  grid-area: 1 / 2 / 2 / 2;
}
  • 实现一个headerslide,main,footer的布局
 <div class="app">
   <header>header</header>
   <aside>slide</aside>
   <main>main</main>
   <footer>footer</footer>
</div>

对应的css如下

.app {
    background: beige;
    display: grid;
    grid-template-columns: 200px 1fr;
    grid-template-rows: 30px calc(100vh - 90px) 60px;
}
header {
    grid-column: 1 / 3;
    background-color: lightBlue;
}
aside {
    grid-column: 1 / 2;
    background-color: pink;
}
main {
    grid-column: 2 / 3;
    background-color: lightYellow;
}
footer {
    grid-column: 1 / 3;
    background-color: palegreen;
}

image.png

justify-self 和 align-self

justify-self 设置某个元素中内容的水平对齐方式。align-self 设置元素中内容的竖直对齐方式。

  • start:内容与网格项的左端对齐
  • end: 内容与网格项的右端对齐
  • center: 内容位于网格项的中间位置
  • stretch: 内容宽度占据整个网格项空间(默认值)

image.png

image.png

place-self

place-self 属性是 align-self 属性和 justify-self 属性的合并简写形式。
语法为:<align-self> <justify-self>