h2:has(+ p) 是合法写法,因 + p 是相对选择器,需依附于前置主选择器;孤立写 :has(+ p) 语法错误。它匹配紧邻后跟 <p> 的 <h2>,中间不能有其他节点。

为什么 :has(+ p) 不能直接写成那样
因为 :has() 的参数必须是一个**相对选择器列表**,而 + p 单独出现时,前面缺少锚定的主选择器——+ 是二元操作符,它左边必须有明确的上下文元素。你不能在 :has() 里孤立地写 + p,浏览器会报语法错误:Invalid selector: :has(+ p)。
h2:has(+ p) 才是合法且有效的写法
要选中「后面紧邻一个 p 元素」的 h2,必须把 h2 放在 :has() 前面,括号里写 + p 表示“这个 h2 后面紧跟着一个 p”:
h2:has(+ p) {
margin-bottom: 0.5rem;
}
- ✅ 匹配
<h2>标题</h2><p>内容</p>中的h2 - ❌ 不匹配
<h2>标题</h2><div>分隔</div><p>内容</p>(中间有div) - ❌ 不匹配
<p>内容</p><h2>标题</h2>(顺序反了) - ⚠️ 注意:Vue/React 渲染后可能插入注释节点(如
<!--v-if-->),导致 DOM 中h2和p不再紧邻,:has(+ p)失效
想选中所有「后面有 p」的标题,但不局限于 h2
可以用属性选择器或通配,但需注意语义和权重:
-
[role="heading"]:has(+ p)—— 依赖 ARIA 属性,需确保 HTML 正确标注 -
section > *:is(h1, h2, h3):has(+ p)—— 限定在section直接子元素中匹配多级标题 -
article > :where(h1, h2, h3):has(+ p)——:where()降低权重,避免覆盖已有样式
不要用 *:has(+ p),它会匹配到所有满足条件的元素(包括 div、span 等),污染范围过大,调试困难。
立即学习“前端免费学习笔记(深入)”;
替代方案:不用 :has() 也能解决的场景
如果只是想控制标题和段落之间的间距,更稳定的做法是直接给 p 设上边距:
h2 + p {
margin-top: 0.75rem;
}
这种写法兼容性更好(IE7+),不依赖 :has(),也不受动态插入节点影响。只有当你需要「基于后续兄弟反向控制当前标题自身样式」(比如加图标、改颜色、隐藏等),才必须用 :has(+ p)。
真正容易被忽略的是:DOM 节点是否真的紧邻——不是看 HTML 源码换行,而是看 Elements 面板里两个节点之间有没有其他 node(哪怕只是文本节点或注释)。这点在框架渲染后尤其关键。


















