Featured image of post Cleverly use attribute selectors to implement filters in pure CSS

Cleverly use attribute selectors to implement filters in pure CSS

Implement filters in pure CSS through the :has() pseudo-class and attribute selectors

Take a look at the effect of this example first

๐Ÿ‰
๐ŸŠ
๐Ÿˆ
๐Ÿ‡
๐Ÿฅ
๐Ÿ‹
โ€๐ŸŒ
๐Ÿ
๐Ÿฅญ
๐ŸŽ
๐Ÿ
๐Ÿ
๐Ÿ‘
๐Ÿ’
๐Ÿ“
๐Ÿซ
โ€๐Ÿ…
๐Ÿซ’

:has() pseudo-class and attribute selector

Let’s learn about these two selectors through the following small example

Attribute selector

I am
I am
1
2
3
4
5
6
7
<!-- Pseudo-elements will be added when the mouse touches them -->
<div linkto='box1'>I am</div>
<div linkto='box2'>I am</div>
<style>
div[linkto='box1']:hover::after{content:'box1'}
div[linkto='box2']:hover::after{content:'box2'}
</style>

In this example, the two divs do not have any class names, but are given a custom attribute linkto. We can use this to select them through [linkto='box1'] and assign pseudo elements.

How to use

In addition to the exact match of a certain attribute value in this example, attribute selectors have many other uses:

Match attribute name

img[alt] This selector will select all img elements with an alt attribute, regardless of the alt content.

Exactly match attribute

img[hidden="true"] This selector will select all img elements with a hidden attribute and a value of true. We can easily hide the element by adding the display:none style to this selector.

Match the existence of a certain attribute value

img[tag~="hd"] This selector can find all img elements with a tag attribute and a value of hd. For example, the selector above can select elements such as <img tag="hd cover"/> and <img tag="sport football hd"/>.

Match attribute values โ€‹โ€‹with a certain prefix

There are two ways to match prefixes: span[lang|="zh"]

This selector can select all span elements whose lang attribute values โ€‹โ€‹start with ‘zh-’, such as <span lang="zh-TW">, <span lang="zh-CN">.

img[type^="low"]

This selector can select all img elements whose type attributes start with ’low’, such as <img type="lowPower"/>, <img type="lowLevel"/>. The difference between it and the above is that it can match words that are not separated by ‘-’.

Match attribute values โ€‹โ€‹with a certain suffix

img[type$="ball"]

This selector can select all img elements whose type attributes end with ‘ball’, such as <img type="football"/>, <img type="basketball"/>.

Match attribute values โ€‹โ€‹containing a certain string

img[type*="0"]

This selector can select all img elements whose type attribute contains ‘0’, such as <img type="110"/>, <img type="4008208820"/>.

:has() pseudo class

I am
I am
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<div class="cont-box">
<!-- Touch these boxes, the disply-box below will display pseudo elements -->
<div linkto='box3'>I am</div>
<div linkto='box4'>I am</div>
</div>
<div class="disply-box"></div>
<style>
    .cont-box:has(>[linkto="box3"]:hover) + .disply-box::after{content:'box3'}
    .cont-box:has(>[linkto="box4"]:hover) + .disply-box::after{content:'box4'}
</style>

Because the hovered element is wrapped by cont-box, the father’s brothers cannot be selected directly through the subsequent sibling selector. We can give the father :has() pseudo-class to indirectly select.

About the :has() selector

When the selector in the brackets is true, the pseudo-class will take effect.

h1:has(+ p)

In this example, the adjacent sibling selector is put in. When a h1 tag is followed by a p tag, this pseudo-class will take effect. In this way, you can give styles to the h1 tag before the p tag while other h1 tags are not affected.

.cont-box:has(>[linkto="box4"]:hover)

Let’s interpret the selector in the example; the content in the brackets is >[linkto="box4"]:hover, which means that there is a child element, the linkto attribute value of this child element is box4, and it is being touched by the mouse. Combined with the :has() pseudo-class, it is to find the .cont-box with such a child element and give it a style.

Complete code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<div class="filter">
	<input checked type="radio" name="filter" id="all"/>
	<label for="all">All</label>
	<input type="radio" name="filter" id="yellow"/>
	<label for="yellow">Yellow</label>
	<input type="radio" name="filter" id="red"/>
	<label for="red">Red</label>
	<input type="radio" name="filter" id="green"/>
	<label for="green">Green</label>
	<input type="radio" name="filter" id="purple"/>
	<label for="purple">Purple</label>
</div>
<div class="fruit-cont">
	<div c="r">๐Ÿ‰</div><div c="y">๐ŸŠ</div><div c="g">๐Ÿˆ</div>
	<div c="p">๐Ÿ‡</div><div c="g">๐Ÿฅ</div><div c="y">๐Ÿ‹</div>
	<div c="y">โ€๐ŸŒ</div><div c="y">๐Ÿ</div><div c="y">๐Ÿฅญ</div>
	<div c="r">๐ŸŽ</div><div c="g">๐Ÿ</div><div c="g">๐Ÿ</div>
	<div c="r">๐Ÿ‘</div><div c="r">๐Ÿ’</div><div c="r">๐Ÿ“</div>
	<div c="p">๐Ÿซ</div><div c="r">โ€๐Ÿ…</div><div c="g">๐Ÿซ’</div>
</div>
<style>
	.filter{display: flex;cursor: pointer;user-select: none;}
	.filter label{
		width: calc(25% - 12px);
		box-sizing: border-box;
		margin: 6px;
		height: 40px;
		line-height: 36px;
		text-align: center;
		border-radius: 8px;
		border: 2px solid;
	}
	.filter input{display: none;}
	.fruit-cont{display: flex;flex-wrap: wrap;}
	.fruit-cont div{
		display: none;
		width: calc(25% - 12px);
		box-sizing: border-box;
		margin: 6px;
		height: 60px;
		line-height: 60px;
		text-align: center;
		font-size: 30px;
		text-shadow: 0 0 6px white;
		border-radius: 8px;
	}
	.filter:has(>#yellow:checked) ~ .fruit-cont div[c="y"]{display: block}
	.filter:has(>#green:checked) ~ .fruit-cont div[c="g"]{display: block}
	.filter:has(>#red:checked) ~ .fruit-cont div[c="r"]{display: block}
	.filter:has(>#purple:checked) ~ .fruit-cont div[c="p"]{display: block}
	.filter:has(>#all:checked) ~ .fruit-cont div{display: block}
	.fruit-cont div[c="r"]{background-color: #e74c3c}
	.fruit-cont div[c="g"]{background-color: #2ecc71}
	.fruit-cont div[c="p"]{background-color: #9b59b6}
	.fruit-cont div[c="y"]{background-color: #f39c12 }
	.filter label[for="red"]{border-color: #e74c3c;color: #e74c3c;}
	.filter label[for="green"]{border-color: #2ecc71;color: #2ecc71;}
	.filter label[for="purple"]{border-color: #9b59b6;color: #9b59b6;}
	.filter label[for="yellow"]{border-color: #f39c12 ;color: #f39c12 ;}
	.filter label[for="all"]{border-color: black;color: black ;}
	.filter input:checked + label[for="red"]{background-color: #e74c3c;color: white;}
	.filter input:checked + label[for="green"]{background-color: #2ecc71;color: white;}
	.filter input:checked + label[for="purple"]{background-color: #9b59b6;color: white;}
	.filter input:checked + label[for="yellow"]{background-color: #f39c12 ;color: white;}
	.filter input:checked + label[for="all"]{background-color: black ;color: white;}
</style>
Licensed under CC BY-NC-SA 4.0
Last updated on Oct 18, 2024 00:00 UTC
Built with Hugo
Theme Stack designed by Jimmy