纯CSS实现多级标签样式
发布时间:
最后更新:
1.多级分类标签介绍 #
在一个分类系统中,类别往往具有从属关系,比如生物属于自然界,动物属于生物,猫属于动物,你家里的叫咪咪的那只又属于猫。现在你建立了一个网站,让每个人都能够展示自己的某个东西,在名称的旁边有一个小标签,显示出了这一趟的从属关系。
本博客在编写时就遇到了这样的需求,需要在首页https://kaciras.net/的文章列表中显示出完整的分类路径,本以为是个很简单的问题,动手之后才发现并没有那么容易,特别是存在点击区域和覆盖的问题,在苦战3个小时之后终于实现了一个比较完美的结果,效果如下(GIF录制问题,实际上内部并不会变色):
本实现具有以下优点:
- 纯CSS,干净无污染
- 三角形部分是真正的不规则(非矩形盒子)元素,点击更精确
- 一键使用,仅需在容器上加上一个class即可
唯一的遗憾就是HTML中的顺序必须反过来,因为HTML中下面的元素Z-Index默认比上面的高。最终版代码如下: 多级分类标签展示
<div class="tag-group">
<a>4级分类</a>
<a>3级分类</a>
<a>二级分类</a>
<a>一级分类</a>
<a>顶级分类</a>
</div>
CSS代码如下,直接复制即可使用:
.tag-group > * {
position: relative;
cursor: pointer;
border: solid 1px #ffa900;
padding: .2em .4em;
}
.tag-group > *:last-child {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.tag-group > *:first-child {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.tag-group > *:not(:last-child) {
padding-left: 1.2em;
border-left-width: 0;
}
.tag-group > *:not(:first-child):after {
content: "";
position: absolute;
top: 50%;
right: -1.2em;
border: solid .65em transparent;
border-top-color: #ffffff;
border-right-color: #ffffff;
transform: translateX(-50%) translateY(-50%) rotate(45deg);
box-shadow: 1px -1px 0 0 #ffa900;
}
.tag-group > *:hover {
color: #ffffff;
background-color: #ffa900;
}
.tag-group > *:hover:after {
border-top-color: #ffa900;
border-right-color: #ffa900;
}
当然我在开发时使用的是LESS
@color-tags: #ffa900;
@color-tag-background: white;
.tag-group {
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
background-color: @color-tag-background;
& > * {
position: relative;
cursor: pointer;
border: solid 1px @color-tags;
padding: .2em .4em;
&:last-child {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
&:first-child {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
&:not(:last-child) {
padding-left: 1.2em;
border-left-width: 0;
}
&:not(:first-child) {
&:after {
content: "";
position: absolute;
top: 50%;
right: -1.2em;
border: solid .65em transparent;
border-top-color: @color-tag-background;
border-right-color: @color-tag-background;
transform: translateX(-50%) translateY(-50%) rotate(45deg);
box-shadow: 1px -1px 0 0 @color-tags;
}
}
&:hover {
color: @color-tag-background;
background-color: @color-tags;
&:after {
border-top-color: @color-tags;
border-right-color: @color-tags;
}
}
}
}
2.三角形的边框实现 #
方形的边框谁都会,所以要做出这么一个效果,关键点就在两个分类之间的三角部分。在网上查阅一些资料后,发现了一个Bootstarp实现三角形的方法。其原理则是利用了边框的分割方式,元素的上下左右四个边框在交界处是以45°为界分开,就像下面这样:
如果边框宽度等于元素宽度的一半,那就刚好可以以X型4等分:
这4个边框就是4个三角形,刚好上下左右各一个,要用哪一个就把其他三个设为透明即可,另外由于边框本身占有宽度,所以元素的width属性也可以设置为0.
这种方式虽然能做出三角形的效果,但是元素本身还是个方形的,在鼠标所在区域判定上,透明的部分仍会被算作当前元素,多个HTML元素挨在一起时会出现看似点击右边,实际上鼠标落在左边的情况。
可惜我们的多级标签需要一个点击功能,点击后跳转到相应分类的页面下,如果采用这种方法,就有可能跳到错误的位置,并且在使用hover伪类实现动态效果上也会让人感觉很不好。
3.CSS3元素旋转实现三角形 #
CSS3新增一个transform属性,它可以有好几个值,专门用来做形状变换:
- translate(X,Y):根据给定的 left(X) 和 top(Y),从其当前位置移动元素。
- rotate(deg):将元素顺时针旋转一定的角度
- scale(X,Y):将元素沿X轴和Y轴缩放指定的倍数
- skew(Xdeg,Ydeg):将元素沿X轴和Y轴翻转指定的角度
- matrix(...):旋转、缩放、移动以及倾斜元素的合并写法
使用transform,可以实现非方形盒子元素,比如transform: rotate(45deg)
就可以做出一个菱形:
接下来把背景颜色设置为白色,并加上上边框和右边框:
可以看到一个三角形的边框已经出来了,并且元素覆盖的区域也随之一起旋转,在斜线外点击是不会被算作点击该元素的。接下来把它作为标签的附加内容:after
即可实现右三角边框:
4.边框+阴影实现半边透明 #
边框做好了,但是当你往里面放几个字的时候又出现问题了。
伪元素把内容给挡住了(╬ ̄皿 ̄),这还了得?赶紧想办法。
首先可以设置一个右填充,但是这样标签右边空白就太多了,显得很难看。那能不能用z-index
实现呢?在分别给他俩设置了z-index
后,什么卵用都没有。
当然如果单独给:after
设置一个负数z-index
是可以让它跑到下面去的,但负数z-index
尽量不要用,因为这将可能导致元素被其他没有设置z-index
的元素覆盖。
既然没法把伪元素挪到下面去,就得换个思路,首先直接把伪类元素设置为透明不可取,因为后面还要做:hover
的高亮效果,必须要求伪类元素能够设置颜色。不过如果能把菱形左半三角去掉的话位置差不多也够了,那应该怎么做呢?回想一下第二节那个假三角的实现,用它可以把元素盒子以X形分开,那么上和右这两个部分的边框在旋转后不正好组成右半边三角吗?
不过既然把边框用来设置背景,那么原本需要的边框怎么办呢?这里需要另一个技巧,就是拿阴影来做边框,我们都知道box-shadow
有6个属性:
- 前两个是设置阴影偏移,把它设置为
1px -1px
的话表示阴影向右上角(X向右,Y向上)移动一个像素。 - 第三个参数是模糊距离,这里不需要模糊,故设为0
- 第四个是阴影尺寸,把它设置为0表明阴影跟元素一样大,然后依靠偏移把它往右上挪,就能实现只显示出右上1个像素的阴影
- 最后一个参数不填,表示阴影在边框外
.kaciras.net:after{
border: 14px solid transparent;
border-top-color: #e3ff82;
border-right-color: #ff8ec6;
box-shadow: 1px -1px 0 0 #0092ff;
position: absolute;
top: 50%;
right: -26px;
content: "";
transform: translate(-50%, -50%) rotate(45deg);
}
经过一番折腾之后,菱形的左半边终于被消灭,终于成功搞定这个很小的三角。
5.多标签堆叠覆盖 #
一个成功不等于全部成功,当你组合使用时就会发现...右边的比左边不知道高到哪里去了,当右边高亮的时候左边的三角部分被遮挡住了。为什么会这样呢?因为后面的元素默认比前面的z-index
要高,这是HTML规范钦定的。
要解决这个问题,首先可以手动指定两者的z-index
值,把前面的设置的比后头的大即可。但是本站是无限级分类,也就是说标签可能有无数多层(虽然在实际中很少有超过5层的分类的博客,但技术就是追求完美),手动设置就显得NAIVE。
这个问题我想了很久也没想出个完美的办法,只能退求其次,让HTML里顺序反过来,在显示上再反一遍,即反序排列。
这个就比较简单了,CSS中的flex布局是一个非常灵活的排版方式,其中flex-direction
就能够让元素反着排列,再加上justify-content
把浮动方向再反一次,即可实现反序排列。
6.结语 #
一个多级标签,看似虽小,实现起来却并不简单。一个前端,到处都是坑,不过技术也正是在一次次踩坑中逐渐成长。而本博客也在一次次更新中完善,到此,多级分类的标签功能成功搞定,关于无限级分类的数据库设计将在另一篇文章中讲解。