CSS 选择器选择前一个兄弟元素

发布时间:9/10/2025
更新时间:9/10/2025
展示:378

使用 :has

CSS 中不能直接通过后一个元素选择前一个元素,只能通过前一个元素选择后一个元素,类似 +~

但是 CSS 提供了一种针对引用元素选择父元素或者先前的兄弟元素的方法。就是 :has 选择器。

:has 是一个函数式伪类选择器,用于选择满足条件的锚定元素,这里的锚定元素指的是使用 :has 的元素。

例如现在有两个元素

<div class="first"></div>
<div class="second"></div>

想要实现:在鼠标移入 second 元素时,first 元素对应的背景修改为红色。

就可以使用 :has 这样写

.first:has(+ .second:hover) {
  background-color: red;
}

这样巧妙把问题转换为:选择满足条件的 first 元素,这里的条件就是:后面紧跟一个鼠标悬浮态的 second 元素。

可容错相对选择器列表

:has 是一个函数式伪类,所以我们可以把它看作是一个函数,那么是函数的话就可以传递参数。

:has 接收的参数就是一个可容错相对选择器列表

什么是可容错相对选择器列表?

这就得先从选择器列表开始说起。

选择器列表

在CSS中,选择器列表指的是用逗号分隔的多个选择器,例如 div, p 。这在我们给多个元素设置相同的样式时非常方便。

/* 原来要这样写 */
div{
  color: red;
}
p{
  color: red;
}

/* 使用选择器列表可以共享相同的声明 */
div, p {
  color: red;
}

但是选择器列表有一个问题,就是如果列表中的任何一个选择器无效,那么整个列表就无效

/* 我们使用了不存在的detail标签 */
div, div>detail, p {
  color: red;
}

这时候 div 和 p 标签的样式就不会生效。

可容错

为了解决这个问题,CSS 引入了可容错选择器列表的概念。 即使其中一个选择器无效,整个列表也会生效。像 :is():where() 伪类 它们就接受一个可容错选择器列表作为参数。

:is(div, div>detail, p) {
  color: red;
}

即使 div>detail 这个选择器无效,div 和 p 标签的样式也会应用样式。

相对

那可容错相对选择器列表的“相对”又是什么意思?

其实就是说这个CSS选择器,它不是从文档根开始找,而是相对于某个‘锚点’元素(也就是你应用 :has() 的那个元素)来查找其内部的元素。

再反过头来看可容错相对选择器列表,我们就可以给它下个结论性的定义:

可容错相对选择器列表就是可以接受错误,并且相对于锚定元素查找的选择器列表。

一些使用场景

现在知道了 :has 的基本使用后,我们可以根据其特点用在一些场景中。

无效字段高亮父容器

<div class="input-group">
  <label for="email">Email*</label>
  <input type="email" id="email" required>
  <span class="error-message">请输入有效的邮箱地址</span>
</div>
/* 当input无效且获得焦点时,高亮其父容器 */
.input-group:has(input:invalid:focus) {
  border-color: red;
  background-color: #ffe6e6;
}

/* 同时显示错误信息 */
.input-group:has(input:invalid:focus) .error-message {
  display: block;
}

根据输入状态改变标签样式

<div class="input-group">
  <label for="email">Email*</label>
  <input type="email" id="email" required>
</div>
/* 输入框获得焦点时,将其对应的标签变为蓝色 */
.input-group:has(input:focus) label {
  color: #0066cc;
}

条件性网格布局

根据子元素数量或类型动态改变布局。

<div class="card-grid">
  <div class="card">...</div>
  <div class="card">...</div>
  <div class="card large">...</div> <!-- 一个大卡片 -->
</div>
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}

/* 如果网格中包含 .large 卡片,则改为两列布局 */
.card-grid:has(.card.large) {
  grid-template-columns: 1fr 1fr;
}

作用父元素兄弟节点的子元素

当鼠标悬停在 .second 元素的 p 标签上时,.first 元素的 p 标签会变成红色。

<div class="first">
  <p>我是p1</p>
</div>
<div class="second">
  <p>我是p2</p>
</div>
.first:has(+ .second>p:hover) > p {
  color: red;
}

需要注意的是:在 :has() 函数内部,不能再嵌套使用 :has()。

这主要是出于性能考量。:has() 本身就是 CSS 中最复杂、最耗性能的选择器之一,因为它打破了浏览器“从左到右”解析 CSS 选择器的常规模式。

浏览器通常的解析方式是:
div > p -> 先找到所有 div,然后在每个 div 里找 p。

而 :has() 的解析逻辑更像是:
div:has(p) -> 先找到所有包含 p 的 div。这需要浏览器做更多“回溯”或“记录”的工作。

如果允许嵌套,比如 :has(:has(:has(...))),会形成巨大的递归查询,计算复杂度会呈指数级增长,可能会使页面样式计算变得极其缓慢,甚至导致浏览器卡死。

目录
  • 使用 :has
  • 可容错相对选择器列表
    • 选择器列表
    • 可容错
    • 相对
  • 一些使用场景
    • 无效字段高亮父容器
    • 根据输入状态改变标签样式
    • 条件性网格布局
    • 作用父元素兄弟节点的子元素