欢迎关注我的公众号:前端侦探
分享一些超级实用的布局小技巧。
平时开发过程中会碰到各式各样的布局,如下是一个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;
}
注意这里只设置了水平方向上的定位(left
和right
),垂直方向上仍然保持原位,我称之为不完全绝对定位,关于这个小技巧,可以参考之前的这篇文章:
效果如下

由于不能使用垂直方向定位,这里就用transform
实现,然后可以用:not
排除掉第一行的元素,具体实现如下
css
.grid-item:nth-child(7n+1):not(:first-child)::after{
/*...*/
transform: translateY(-15px);
}
效果就很完美了,实现也比较简单

你也可以访问在线demo真实体验:codepen.io/xboxyan/pen...
四、总结一下
以上就本文的全部内容了,非常实用的布局小技巧,其实都是选择器的实际运用,你学会了吗?下面总结一下
grid
布局无法直接选择行- 嵌套的方式实现分隔线有些繁琐,不够优雅
- 渐变可以很方便的实现平铺的线条
- 默认情况下,渐变会从
padding-box
开始平铺,也会充满整个padding-box
- 使用伪元素可以方便的控制尺寸,可以无需改变
padding-box
来控制渐变的起始点,也能控制渐变的平铺区域 grid
布局最后一行可以一一列举处理,比如当最后一行只有一个元素时其实是最后一个元素并且是每一行的第一个- 最后一行只有两个元素的条件是倒数第二个元素并且是每一行的第一个
- 还可以借助伪元素的不完全绝对定位贯穿整行
grid
布局现在已经非常普遍了,碰到这种布局都可以尽量用朝这方面考虑,比嵌套二维数组要高效地多。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发 ❤❤❤
欢迎关注我的公众号:前端侦探