一般的,我们在选取子元素时可以使用 a>img 这样的选择器,而反过来如果需要选择拥有图片的 a 元素,就只能通过 JavaScript 来实现了,不过现在就可以使用 has 选择器来实现这个需求。

准确的说,这个选择器很早就被提出,但是由于性能问题一直未曾实现,直到 22 年中旬才真正可以在浏览器使用,目前最新的 Chrome 和 Safari 均已默认支持,Firefox 开启实验支持后也能够使用,原则上已经可以稍稍使用一下了,当然生产环境的话估计还需要一两年才能无痛使用。

浏览器兼容性

根据定义,给定的选择器参数相对于该元素(:scope)至少匹配一个元素,MDN 上的示例:匹配其后紧跟着 <p> 元素的 <h1> 元素:

相当于有个 :scope 而非是 h1:has(h1 + p)
h1:has(+ p) {
//...
}

选择器参数可以使用所有的 CSS 选择符,在多个选择器时,组内是或的关系,例如 h3:has(a, img) 匹配的是有 a 标签或者 img 标签的 h3 元素。同时 :has() 伪类还可以和 :checked, :enabled 等混用,比如:

当 .switch-input 被选择后更改 body 的背景色
body:has(.switch-input:checked) {
background-color: #000;
}

总而言之,:has() 能够用到的场景非常多,不过由于是最近被支持的新特性,也只能在个人环境下使用一二了,经典的拿来写样式去除页面不喜欢的元素,比如哔哩哔哩的个人动态页面,我希望屏蔽所有的除视频外的动态(一般是 UP 主推的广告):

/* 屏蔽除了投稿视频外的任何内容 */
.bili-dyn-list__item:not(:has(a.bili-dyn-card-video)) {
display: none
}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Has 兼容性测试</title>
<style>
body {
background-color: #f4f4f4;
}
body:has(.switch-input:checked) {
background-color: #14171a;
}
.switch {
width: 80px;
margin: 300px auto;
}
.switch-input {
display: none;
}
.switch-label {
display: block;
width: 80px;
height: 40px;
border: 2px solid #ccc;
border-radius: 20px;
padding: 2px;
box-sizing: border-box;
}
.switch-label::after {
display: block;
content: " ";
width: 50%;
height: 100%;
position: relative;
background-color: gray;
border-radius: 30px;
transition: all .3s ease-in;
left: 0;
}
.switch-input:checked + .switch-label::after {
left: 50%;
}
</style>
</head>
<body>
<div class="switch">
<input type="checkbox" id="switch-input" class="switch-input">
<label for="switch-input" class="switch-label"></label>
</div>
</body>
</html>

评论