Cleverly use attribute selectors to implement filters in pure CSS

Original article: Cleverly use attribute selectors to implement filters in pure CSS

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
<!-- 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
<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

<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>
posted @ 2025-06-15 13:51  粥粥粥菌  阅读(14)  评论(0)    收藏  举报