、:is() 和 :where() 实战)
CSS 现代选择器的工程威力:has()、:is() 和 :where() 实战一、:has()父子选择器的方向反转:has()选择器是 CSS 历史上最重要的功能补充之一。它的核心能力是根据子元素状态选择父元素实现了 CSS 选择器方向上的反转。在:has()之前CSS 只能向下选择父选子有了:has()之后可以从子元素的状态向上传递样式影响。最典型的应用场景是表单字段的错误状态联动。当输入框出现错误提示时不仅输入框本身需要红色边框其所在的表单组容器也需要相应的视觉反馈/* 表单组的错误状态联动 —— 使用 :has() */ .form-group { padding: 12px; border: 1px solid #e5e7eb; border-radius: 8px; transition: border-color 0.2s ease, box-shadow 0.2s ease; } /* 当表单组内的某个输入框处于错误状态时 整个表单组的边框和阴影都变为错误样式 */ .form-group:has(.input-error) { border-color: #ef4444; box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1); } /* 当表单组内的错误提示可见时统一处理 */ .form-group:has(.error-message:not([hidden])) { border-color: #ef4444; } /* 组合条件表单组包含错误状态的输入框 AND 不包含 disabled 状态 */ .form-group:has(.input-error):not(:has([disabled])) { animation: shake 0.3s ease; } keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }另一个高价值的场景是卡片布局的自动适配。当卡片中包含图片时布局自动切换为左右结构否则保持单列布局/* 自适应卡片布局有图时左右排列无图时上下排列 */ .product-card { display: flex; flex-direction: column; padding: 16px; background: #fff; border-radius: 12px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } /* 关键逻辑如果卡片内存在图片元素切换为水平布局 */ .product-card:has(img) { flex-direction: row; align-items: center; gap: 16px; } .product-card:has(img) .product-image { flex-shrink: 0; width: 120px; height: 120px; } .product-card:has(img) .product-info { flex: 1; }二、:is() 和 :where()复杂选择器的简洁化与优先级控制:is()和:where()都用于将多个选择器合并为一个减少重复声明。两者的差异在于特异性计算方式——:is()的特异性等于其参数中特异性最高的那个选择器而:where()的特异性始终为 0。/* 使用 :is() 合并重复的选择器组 */ /* 传统写法每个选择器独立声明 */ .page-header h1, .page-header h2, .page-header h3, .section-title h1, .section-title h2, .section-title h3 { font-weight: 600; color: #1a1a2e; letter-spacing: -0.02em; } /* 使用 :is() 的等价写法 */ :is(.page-header, .section-title) :is(h1, h2, h3) { font-weight: 600; color: #1a1a2e; letter-spacing: -0.02em; }:where()的零特异性特性尤其适合定义全局默认样式。这些样式可以被任何更高特异性的规则覆盖而不需要担心优先级冲突/* 使用 :where() 定义低优先级的默认样式 */ /* 这些样式很容易被覆盖适合作为基础样式层 */ :where(button, .btn) { padding: 8px 16px; border: 1px solid transparent; border-radius: 6px; font: inherit; cursor: pointer; transition: background-color 0.2s ease; } /* 下面的样式不需要提高特异性就能覆盖默认样式 */ .btn-primary { background-color: #2563eb; color: #fff; } .btn-primary:hover { background-color: #1d4ed8; }下表总结了三种选择器的特异性行为差异flowchart LR A[选择器写法] -- B[特异性计算] B -- C{选择器类型} C --|:is(h1, .title)| D[取最高特异性br/对应 .title 的特异性] C --|:where(h1, .title)| E[特异性始终为 0br/不参与优先级累加] C --|:has(.error)| F[与 :is() 相同br/取参数中最高特异性]三、组合应用减少 JS 样式逻辑的 CSS 原生方案现代选择器的组合使用可以在不引入 JavaScript 的前提下实现许多原本需要状态管理才能完成的效果。一个常见的需求是复选框选中后相邻区域的内容展开。在:has()之前这个效果需要 JavaScript 监听 checkbox 的 change 事件并切换 CSS 类名/* 使用 :has() 兄弟选择器实现无 JS 的展开/收起效果 */ .collapsible-section { border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; } /* 隐藏的实际内容区域 */ .collapsible-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; } /* 当隐藏的 checkbox 被选中时展开内容区域 */ .collapsible-section:has(.collapse-toggle:checked) .collapsible-content { max-height: 1000px; /* 足够大的值实际内容不会超过 */ } /* checkbox 本身隐藏通过 label 进行交互 */ .collapse-toggle { position: absolute; opacity: 0; pointer-events: none; }另一个案例是空状态的条件渲染。当列表中没有内容时显示空状态占位符/* 无 JS 的空状态展示 */ .data-list { display: grid; gap: 12px; } /* 当列表中没有任何内容项时显示空状态 */ .data-list:not(:has(.list-item))::after { content: 暂无数据; display: flex; align-items: center; justify-content: center; padding: 48px 0; color: #9ca3af; font-size: 14px; grid-column: 1 / -1; }四、浏览器兼容与现实约束截至 2026 年 7 月:has()在所有主流浏览器的较新版本中都已获得支持。Chrome 105、Firefox 121、Safari 15.4 均原生支持全球覆盖率约 95%。:is()和:where()的兼容性更早达成Chrome 88、Firefox 78、Safari 14 均已支持覆盖率约 98%。但在生产项目中仍然需要注意渐进增强策略。对于使用这些选择器的关键布局逻辑应提供降级方案/* 渐进增强提供不支持 :has() 时的降级方案 */ /* 降级方案使用常规类名所有浏览器生效 */ .form-group-error { border-color: #ef4444; box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1); } /* 增强方案使用 :has()支持 :has() 的浏览器自动覆盖 */ supports selector(:has(*)) { .form-group:has(.input-error) { border-color: #ef4444; box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1); } /* 支持 :has() 的浏览器中禁用降级方案的显示 */ .form-group-error { border-color: #e5e7eb; box-shadow: none; } }五、总结:has()解决了 CSS 选择器方向上的历史性限制使得根据子元素状态影响父元素样式成为原生能力大幅减少了对 JavaScript 样式逻辑的依赖。:is()和:where()通过选择器合并减少了样式表的冗余度其中:where()的零特异性特性是构建可维护的样式优先级体系的关键工具。这三者组合使用能够在不增加 JavaScript 体积的前提下实现条件布局自适应、交互状态联动、空状态展示等常见 UI 需求。在生产环境中建议通过supports检测提供兼容降级方案确保在不支持:has()的浏览器中关键功能仍能正常运作。