CSS 新特性 :has() 选择器,不需要 JS 也能实现神奇交互

以前是无法直接用 CSS 来“选择父元素”的,而 :has() 选择器的出现,刚好改变了这一现状。

下面,我们一起看看:has()选择器是如何使用的吧。

一、介绍

:has()其实就是一个伪类选择器,它可以选择包含特定子级元素的元素。

听起来可能有点绕,我们直接看它的语法吧:

/* 选择“包含 img 子元素”的 article 元素 */
article:has(img) {}

/* 选择“直接子元素是 figcaption”的 figure 元素 */
figure:has(> figcaption) {}

/* 选择“包含空的 p 元素”的 div */
div:has(p:empty) {}

你也可以这样理解,它就是选择“满足特定条件”的父级元素,两个重点:

  1. 选择的是父元素
  2. 子元素只是作为条件

为什么说它强大?

因为它打破了过去“CSS 只能向下选择”的限制,能够通过子元素的状态来改变父级元素的样式

二、实战案例

下面看两个比较常见的案例吧。

1️⃣ 卡片高亮效果

假如有一个卡片,需要实现当“鼠标悬停某个子元素时”,整个卡片进行高亮。

<!-- 一个简单的卡片 -->
<section class="card">
  <h2>卡片标题</h2>
  <p>这是一段描述文字...</p>
</section>
.card {
  padding: 8px 16px;
  width: 300px;
  border: 1px solid #ddd;
  border-radius: 16px;
}

/* 当悬停在 p 元素上时,整个卡片高亮 */
.card:has(p:hover) {
  border-color: #007bfe;
  box-shadow: 0 4px 12px rgba(0, 121, 251, 0.3);
}

👉 最终效果:只有鼠标放在<p>标签上时,卡片才会高亮。

卡片高亮效果
卡片高亮效果

2️⃣ 输入框校验报错提示

这个场景大家应该都遇到过,当表单项输入报错时,需要对整个表单项区域做一个视觉反馈效果。

<div class="form-item">
  <label for="email">邮箱地址:</label>
  <input type="email" id="email" required>
  <span class="hint">请输入有效的邮箱地址</span>
</div>
.hint {
  display: none;
  color: red;
  font-size: 14px;
}

/* 当输入框“验证失败”时,显示提示文字 */
.form-item:has(input:invalid:focus) .hint {
  display: block;
}

/* “验证失败”,高亮整个表单项区域 */
.form-item:has(input:invalid:focus) {
  padding: 16px;
  background-color: #fef6f8;
  border-left: 3px solid red;
}

👉 效果预览

表单验证效果
表单验证效果

在以前,用纯 CSS 就无法实现以上这两个案例,必须借助 JS 的能力来完成,现在有了:has()之后,实现起来就简单多了。

三、高级技巧

👉 与相邻选择器组合:

/* 选择后面跟着 <hr> 的 <section> */
section:has(+ hr) { margin-bottom: 0; }

以前,相邻选择器只能选择相邻的“下一个”元素,现在能够实现选择相邻的“上一个” 元素了。

👉 与兄弟选择器组合:

/* 选择除了最后一个子元素以外的所有 .list-item */
.list-item:has(~ .list-item) { border-bottom: 1px solid #eee; }

当然,如果你有更复杂的需求,你也可以与任何选择器组合,从而可以创造出非常精细的样式控制。

四、浏览器支持情况

根据caniuse上面的数据来看,基本上,2022 年之后的主流浏览器都已支持。

浏览器支持情况
浏览器支持情况

当下建议:如果对兼容性比较敏感或者是比较重要的功能,需要谨慎使用;如果只是作为视觉体验上的提升,那么还是很值得一试的。

五、总结

随着 CSS 的不断发展,各种新功能也逐渐推出。以前很多都需要借助 JS 才能实现的能力,现在直接用 CSS 本身就能够完美的实现了。

不需要借助 JS 之后,它也让我们编写的代码更简洁且容易维护。

来源:前端星河

THE END