先记住这个答案
CSS 的容错规则是:一条规则里的选择器如果有任何一部分浏览器不认识,整条规则都会被丢弃。这意味着 .card:has(> img), .card-special { color: red } 在不支持 :has() 的浏览器上,.card-special 的样式也会丢失。解决办法是分开写成两条规则,或者用 @supports selector(:has(> *)) 做特性检测。
- 不认识的选择器让整条规则失效
- 新旧选择器要分开写
- 用
@supports selector()做特性检测 - 私有前缀伪元素不保证跨浏览器一致
为什么不认识的选择器会连累整条规则
CSS 规范规定,选择器列表是一个整体。如果列表中有一个无效的选择器,浏览器没办法确定这个无效部分的意图是什么,为了安全起见会丢弃整条规则。
这个设计在大多数时候是合理的,但在跨浏览器兼容时会造成问题。尤其是私有前缀伪元素,不同浏览器的语法完全不同。
/* 错误:放在一起,Firefox 不认 -webkit 会丢弃整条 */
input::-webkit-input-placeholder,
input::-moz-placeholder {
color: #999;
}
/* 正确:分开写,各自独立 */
input::-webkit-input-placeholder { color: #999; }
input::-moz-placeholder { color: #999; }
/* 现在用标准写法就行 */
input::placeholder { color: #999; }::-webkit-input-placeholder 和 ::-moz-placeholder 是不同浏览器的私有前缀。放在同一个选择器列表里,Firefox 不认 -webkit 的部分,就把整条规则丢了,结果 -moz 的也不生效。分开写就没这个问题。
用 @supports selector() 做特性检测
@supports selector() 可以检测浏览器是否支持某个选择器。把新选择器的样式包裹在里面,不支持的浏览器会直接跳过整个块。
/* 基础样式,所有浏览器都生效 */
.card {
border: 1px solid #ddd;
}
/* 只在支持 :has() 的浏览器生效 */
@supports selector(:has(> *)) {
.card:has(> img) {
border-color: transparent;
}
}这比分开写两条规则更清晰。基础样式保证所有浏览器都有可用的效果,增强样式只在支持新特性时才加载。这就是渐进增强的思路。
is() 和 where() 的容错解析
:is() 和 :where() 自带一个重要特性:容错选择器列表。参数里有一个无效的选择器时,只跳过那一个,其余的照常匹配。
这和传统选择器列表的行为不同。h1, h2::xyz, h3 {} 里 h2::xyz 无效,整条规则丢弃。但 :is(h1, h2::xyz, h3) {} 里 h2::xyz 无效,只跳过它,h1 和 h3 照常匹配。
利用这个特性,可以把标准选择器和实验性选择器放在 :is() 的参数里。旧浏览器不认识实验性的那个,就自动忽略,不影响其他选择器的匹配。
常见的私有前缀伪元素
有几个私有前缀伪元素在实际项目中经常遇到。::-webkit-scrollbar 系列用来自定义滚动条,只在 WebKit 和 Blink 内核(Chrome、Safari、Edge)里有效,Firefox 不支持这套写法。Firefox 用 scrollbar-color 和 scrollbar-width 两个标准属性来简单定制滚动条。
::placeholder 现在已经是标准化的了,但旧版本浏览器还需要 ::-webkit-input-placeholder 和 ::-moz-placeholder。新项目里只写 ::placeholder 就够了。
容易答错的地方
- 以为前缀选择器可以写在同一条规则里
::-webkit-scrollbar和::-moz-xxx不能合并写。如果合并,不认识某一个前缀的浏览器会丢弃整条规则。每种浏览器的私有写法必须各占一条规则。- 以为 @supports 能检测所有兼容性问题
@supports selector()本身也需要浏览器支持。非常老的浏览器连@supports都不认识,会跳过整个块。好在现代浏览器都已支持@supports selector(),主流兼容性问题不用担心。
面试官还会怎么问?
怎么判断一个 CSS 特性的浏览器支持情况?
最常用的工具是 Can I Use(caniuse.com),输入特性名就能看到各浏览器的支持版本。在代码里可以用 @supports 做运行时检测,JavaScript 里用 CSS.supports('selector(:has(> *))') 做同样的判断。
滚动条样式怎么做跨浏览器兼容?
Chrome 和 Safari 用 ::-webkit-scrollbar 系列伪元素可以完全定制滚动条的外观。Firefox 用标准属性 scrollbar-color 和 scrollbar-width,功能比较简单,只能设置颜色和宽度。要做统一的效果,通常用 JavaScript 滚动条库或者用两套规则分别写。
PostCSS 这类工具能帮处理兼容性吗?
可以。PostCSS 的 Autoprefixer 插件能自动添加浏览器前缀。postcss-preset-env 可以让你用新 CSS 特性并自动生成降级代码。但它们只能处理语法层面的降级,选择器兼容性(比如 :has())目前还不能自动转换。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。