CSS grid 布局如何添加分隔线?

欢迎关注我的公众号:前端侦探

分享一些超级实用的布局小技巧。

平时开发过程中会碰到各式各样的布局,如下是一个7* N的网格布局,不过每一行还有一条分隔线

这种布局该如何实现呢?

一、grid 布局结构与局限

这里的内容都是后端返回的,通常是一个完整的一维数组

js 复制代码
const list = [
  {...},
  {...},
  //...
]

众所周知,grid是二维布局,可以在扁平dom结构下直接实现网格布局

html 复制代码
<div class="grid">
  <div class="grid-item">1</div>
  <div class="grid-item">2</div>
  <div class="grid-item">3</div>
  <div class="grid-item">4</div>
  <div class="grid-item">5</div>
  <div class="grid-item">6</div>
  <div class="grid-item">7</div>
  <div class="grid-item">8</div>
  ...
</div>

然后设置grid样式

css 复制代码
.grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  width: 300px;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin: 20px auto;
}

效果如下,很方便就实现一个网格布局

由于每一行还有一条单独的分隔线,看似好像无法处理?毕竟 grid 没有所谓的行选择器

这时,很多同学可能会单独给每一行包一层,合成一个二维数组

js 复制代码
const grouplist = [
  [{...},{...},{...},{...},{...},{...},{...}], // 每一行7个
  //...
]

自然dom结构也需要包裹一层,然后给每一行单独样式。

html 复制代码
<div clas=="list">
  <div class="col"> <!--设置样式-->
    <div></div>
		<div></div>
    <div></div>
		<div></div>
    <div></div>
		<div></div>
  </div>
  <div class="col"> ...</div>
  
</div>

虽然也能实现,但还是有些繁琐,也不够优雅

那么,有没有办法在不嵌套的情况实现分隔线呢?

二、使用渐变实现

我们可以直接绘制重复平铺的渐变,原理其实也很简单

假设每一行的高度固定为--size,那么我们需要绘制一条从透明到实色的渐变,线条的高度是1px,然后背景尺寸是--size,这样渐变就会自动平铺,形成我们所需的网格线效果,原理如下

用代码实现就是

css 复制代码
.grid{
  /**/
  --size: 54px;
  background: linear-gradient(transparent 0 calc(var(--size) - 1px), red) 0 1px/100% var(--size);
}

效果是这样的

看着好像和每一行没对齐?

其实是因为我们设置了padding,渐变会默认从padding-box开始平铺,也就是起始点是从内边距开始的,所以我们要改变一下,让他从内容盒子开始

css 复制代码
.grid{
  background-origin: content-box;
}

效果如下

对齐了,但是上下多了两条,有没有什么办法去掉呢?

还是盒子的问题,渐变默认会充满padding-box,也需要改一下

css 复制代码
.grid{
  background-clip: content-box;
}

这样上右左,只要在padding区域的渐变都被裁剪了,不过地下还是有一条线

因为刚好处于平铺的最下方,仍然位于content-box内,所以没有被裁掉,那有没有办法去掉呢?

其实将整个容器的高度减少1个像素就可以让下方的线条出去了,不过试了几种方式都不能很好解决,没办法,我们将这层背景绘制在一个单独的伪元素上,使用伪元素可以方便的控制尺寸,也无需改变padding-box

css 复制代码
.grid::before{
  content: '';
  position: absolute;
  inset: 10px 10px 11px; /*底部多一像素*/
  background: linear-gradient(transparent 0 calc(var(--size) - 1px), red) 0 0/100% var(--size);
  pointer-events: none;
}

这样就完美实现了

你也可以访问在线demo真实体验:codepen.io/xboxyan/pen...

不过这种方式要求每一行高度是固定,提前算好的,如果每一行的高度会发生变化就不适用了

三、用上下border实现

我们可以给每个子元素设置上边框来实现分隔线效果

css 复制代码
.grid-item{
	border-top: 1px solid #ccc
}

缺点很明显,第一行是多余的,而且当最后一行不足时,边框就断开了,效果如下

我们可以换个方向,用下边框实现

css 复制代码
.grid-item{
	border-bottom: 1px solid #ccc
}

稍微好点了,只是最后一行有点多余,效果如下

如何把最后的尾巴去掉呢?也就是如何选中grid布局的最后一行?

这里可以一一列举,比如当最后一行只有一个元素时(其实是最后一个元素并且是每一行的第一个,需同时满足

css 复制代码
.grid-item:last-child:nth-child(7n+1) {
  border: none
}

同时满足这两个条件即可确定是最后一行的第一个, 示意如下

然后是最后一行只有两个元素(倒数第二个元素并且是每一行的第一个

css 复制代码
.grid-item:nth-last-child(2):nth-child(7n+1), 
.grid-item:nth-last-child(2):nth-child(7n+1)~.grid-item{
  border: none
}

示意如下

依次类推,完整选择器代码是

css 复制代码
/*1*/
.grid-item:last-child:nth-child(7n+1),
/*2*/
.grid-item:nth-last-child(2):nth-child(7n+1), 
.grid-item:nth-last-child(2):nth-child(7n+1)~.grid-item,
/*3*/
.grid-item:nth-last-child(3):nth-child(7n+1), 
.grid-item:nth-last-child(3):nth-child(7n+1)~.grid-item,
/*4*/
.grid-item:nth-last-child(4):nth-child(7n+1), 
.grid-item:nth-last-child(4):nth-child(7n+1)~.grid-item,
/*5*/
.grid-item:nth-last-child(5):nth-child(7n+1), 
.grid-item:nth-last-child(5):nth-child(7n+1)~.grid-item,
/*6*/
.grid-item:nth-last-child(6):nth-child(7n+1), 
.grid-item:nth-last-child(6):nth-child(7n+1)~.grid-item,
/*7*/
.grid-item:nth-last-child(7):nth-child(7n+1), 
.grid-item:nth-last-child(7):nth-child(7n+1)~.grid-item{
  border: 0;
}

这样就能选中最后一行行了,虽然看着很多(别急,后面还有其他方案),但是效果很不错

还有其他情况也表现完美

你也可以访问在线demo真实体验:codepen.io/xboxyan/pen...

四、借助伪元素实现

上面虽然能够实现,也非常灵活,如果仅仅是实现分隔线,其实还有另外更巧妙的方案。

还是前面的结构,我们可以给每一行的第一个元素添加一个伪元素,用伪元素实现分隔线

css 复制代码
.grid-item:nth-child(7n+1)::before{
  content: '';
  display: block;
  height: 1px;
  background-color: #ccc;
}

效果如下

看着好像相差甚远?没关系,我们可以使用绝对定位,需要注意的是,我们需要让伪元素贯穿整行,所以要给外层添加相对定位

css 复制代码
.grid{
  position: relative;
}
.grid-item:nth-child(7n+1)::after{
  content: '';
  position: absolute;
  left: 10px; /*只设置水平方向定位*/
  right: 10px;
  height: 1px;
  background-color: #ccc;
}

注意这里只设置了水平方向上的定位(leftright),垂直方向上仍然保持原位,我称之为不完全绝对定位,关于这个小技巧,可以参考之前的这篇文章:

你可能不知道的绝对定位

效果如下

由于不能使用垂直方向定位,这里就用transform实现,然后可以用:not排除掉第一行的元素,具体实现如下

css 复制代码
.grid-item:nth-child(7n+1):not(:first-child)::after{
  /*...*/
  transform: translateY(-15px);
}

效果就很完美了,实现也比较简单

你也可以访问在线demo真实体验:codepen.io/xboxyan/pen...

四、总结一下

以上就本文的全部内容了,非常实用的布局小技巧,其实都是选择器的实际运用,你学会了吗?下面总结一下

  1. grid布局无法直接选择行
  2. 嵌套的方式实现分隔线有些繁琐,不够优雅
  3. 渐变可以很方便的实现平铺的线条
  4. 默认情况下,渐变会从padding-box开始平铺,也会充满整个padding-box
  5. 使用伪元素可以方便的控制尺寸,可以无需改变padding-box来控制渐变的起始点,也能控制渐变的平铺区域
  6. grid布局最后一行可以一一列举处理,比如当最后一行只有一个元素时其实是最后一个元素并且是每一行的第一个
  7. 最后一行只有两个元素的条件是倒数第二个元素并且是每一行的第一个
  8. 还可以借助伪元素的不完全绝对定位贯穿整行

grid布局现在已经非常普遍了,碰到这种布局都可以尽量用朝这方面考虑,比嵌套二维数组要高效地多。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发 ❤❤❤

欢迎关注我的公众号:前端侦探

相关推荐
OpenTiny社区6 分钟前
直播预告|TinyVue 组件库高级用法:定制你的企业级UI体系
前端·vue.js·开源
David凉宸6 分钟前
一篇文带你使用vue完成一个完整后台
前端
嘻嘻嘻嘻嘻嘻ys7 分钟前
《Spring Boot 3.2 × Java 21虚拟线程:百万并发Web应用实战与性能飞跃》
前端·后端
_志哥_10 分钟前
前端项目离线打包方案
前端·webpack
顾青10 分钟前
在 Electron 中引入本地谷歌插件
前端·electron
顾青11 分钟前
Electron自定义安装脚本
前端·electron
Aiolimp12 分钟前
React中CSS使用方法
前端·react.js
爱上大树的小猪12 分钟前
【前端基础】viewport 元标签的详细参数解析与实战指南
前端·css·面试
前端九哥13 分钟前
✨ 前端实现打字机效果的主流插件推荐
前端
五号厂房14 分钟前
咦?localStorage 还可以这么玩!
前端