CSS :has() 伪类选择器的妙用
前言
前几天水群,有大佬提到 CSS 的 :has()
伪类选择器可以替代 JS 的一些功能,当时感到很震惊,于是学习了一番,写下本文记录一下。
顾名思义, A:has(B)
就是指选中一个和 B 标签相关位置关系的标签 A,例如 p:has(+h1)
就是指选中这样的 p 标签,他的后面有一个相邻的 h1 标签。
下面的代码中,只有第一个 p 标签会被选中。+p
是相邻兄弟选择器。
<style>
p:has(+h1) {
color: red;
}
</style>
<p>text text</p>
<h1>hello world</h1>
<p>text text</p>
再看一个例子,这里的 div:has(>span)
是这样一个 div,它有一个直接后代的 span,第一个 div 是不满足的,所以选中的是 <div><span>222</span></div>
(>span
是直接子代选择器)。
<style>
div:has(>span) {
color: red;
}
</style>
<div><p><span>111</span></p></div>
<div><span>222</span></div>
这里不再叙述 :has()
的基础语法了,不了解的可以去自行搜索。本文主要叙述使用 :has()
代替 JS 的一些操作:
- 点击按钮切换样式
- 表单输入格式验证提示
点击按钮切换样式
需求1:点击按钮,修改标题的颜色。
使用 JS 很容易能够实现这个功能,但现在试着使用 CSS 实现。一开始的思路是使用 :active
配合 :has()
,当具有一个相邻的被点击的标签的时候,选中目标标签。这样就模拟了点击效果,核心代码就是 h1:has(+ button:active)
。
<style>
h1:has(+ button:active) {
color: red;
}
</style>
<h1>I love CSS</h1>
<button>toggle</button>
但是这个效果有问题,只有按住点击的时候(active 状态),才会选中目标标签 h1。有没有一种可以维持住某个状态的 CSS 伪类?可以想到使用 input:checkbox
的 :checked
伪类。原理就是将 label 和 input 绑定,然后隐藏 input,点击 label 后就相当于选中了 checkbox 表单。
<style>
h1:has(+ input[type=checkbox]:checked) {
color: red;
}
#toggle {
display: none;
}
</style>
<h1>I love CSS</h1>
<input type="checkbox" id="toggle">
<label for="toggle">
toggle
</label>
效果如下:点击 toggle 可以修改标题颜色。
表单输入格式验证提示
需求2:检测表单输入邮箱,检查输入内容格式给出用户提示。
要实现这个这个需求,我们需要判断显示不同信息的条件,例如显示一个格式非法提醒:
- input 不是 focus 状态(使用
:not(:focus)
判断) - input 有输入内容(若没有输入内容,则显示 placeholder 内容,可以通过
:placeholder-shown
判断) - 格式非法(输入邮箱不符合基本格式,使用
:invalid
伪类实现)
<style>
label:has(+ :not(:focus):not(:placeholder-shown):invalid) {
color: red;
}
label:has(+ :not(:focus):not(:placeholder-shown):invalid)::before {
content: "❌ ";
}
label:has(+ :not(:focus):not(:placeholder-shown):valid) {
color: green;
}
label:has(+ :not(:focus):not(:placeholder-shown):valid)::before {
content: "✅ ";
}
</style>
<form>
<label for="email">E-mail</label>
<input type="email" name="email" id="email" placeholder=" "/>
</form>
解释第一行 CSS 代码,其他类似。对于 label:has(+ :not(:focus):not(:placeholder-shown):invalid)
这行代码,可以按照上述三个条件理解,即 “没有 focus”、“不是显示 placeholder”、“格式非法”,那么就显示 “❌ E-mail”(红色字体)。
格式正确就显示 “✅ E-mail”:
:has
伪类是比较新的 CSS 特性,在新版本的 chrome 中可以使用,在 firefox 中要开启实验性功能才能起效果,相信未来会全面扩展。