CSS 有一个很酷的功能,它允许我们测试浏览器是否支持特定的属性或属性:值组合,然后再应用样式块 - 就像一个 @media
查询匹配时,比如浏览器窗口的宽度小于某个指定的大小,然后其中的 CSS 就会生效。同样,当正在测试的属性:值对在当前浏览器中受支持时,此功能内的 CSS 就会生效。这个功能叫做 @supports
,它看起来像这样
@supports (display: grid) {
.main {
display: grid;
}
}
为什么呢?好吧,这有点棘手。就我个人而言,我发现自己并没有经常用到它。CSS 有自然的回退机制,这样如果浏览器不理解属性:值组合,那么它就会忽略它,如果存在任何内容,就会使用之前声明的内容,这得益于层叠。有时这可以用来处理回退,最终的结果是更简洁。我当然不是那种希望它在所有浏览器中都保持一致的人,但我也不是那种为了让它们看起来像那种编写详细的回退的人。我通常更喜欢这种情况,即属性:值的自然故障不会造成任何破坏功能的重大影响。
也就是说,@supports
肯定有它的用例!而且当我把这篇文章整理好后,我发现很多人在很多有趣的情况下都使用了它。
一个经典的用例
我在引言中使用的示例是一个经典的示例,你将在很多关于这个主题的写作中看到它。这里稍微详细一点
/* We're gonna write a fallback up here in a minute */
@supports (display: grid) {
.photo-layout {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-gap: 2rem;
}
}
不错的网格!重复和自动填充列是 CSS 网格 的一个很酷的功能。但是,当然,有些浏览器不支持网格,或者不支持我在上面使用的所有特定功能。
例如,iOS 在版本 10.2 中发布了对 CSS 网格的支持,但 iOS 从版本 7 开始就支持弹性盒子。对于那些使用旧版 iOS 设备的用户来说,这是一个很大的差距,他们支持弹性盒子,但不支持网格。我相信还有更多类似的差距,但你可能已经明白了。
我正在使用旧版本的移动版 Safari,很多很多很多很多网站都因为使用了网格而崩溃了
我正在等待一年左右,然后才能开始使用它
— David Wells (@DavidWells) 2019 年 2 月 6 日
根据要求,让它的回退为空可能可以接受。例如,垂直堆叠的块级元素,而不是多列网格布局。我通常可以接受。但是,假设它不可接受,比如一个图片库或一些绝对需要一些基本的网格状结构的东西。在这种情况下,从弹性盒子作为默认值开始,使用 @supports
在支持的地方应用网格功能可能效果更好……
.photo-layout {
display: flex;
flex-wrap: wrap;
> div {
flex: 200px;
margin: 1rem;
}
}
@supports (display: grid) {
.photo-layout {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-gap: 2rem;
> div {
margin: 0;
}
}
}
“回退”是 @supports
块之外的代码(在上面示例中,块上面的属性),而网格代码在里面或后面。@supports
块不会改变任何特异性,因此我们需要源代码顺序来帮助确保覆盖有效。
请注意,我不得不重置 @supports
块内部 div 的边距。这就是我发现有点烦人的地方。这两种情况之间存在足够的重叠,需要非常清楚地了解它们是如何相互影响的。
难道你不希望它能完全逻辑分离吗?……
@supports 块中“没有”逻辑,但这并不意味着它应该总是被使用
Jen Simmons 几年之前在名为 在 CSS 中使用特性查询 的文章中提出了这个示例
/* Considered a BAD PRACTICE, at least if you're supporting IE 11 and iOS 8 and older */
@supports not (display: grid) {
/* Isolated code for non-support of grid */
}
@supports (display: grid) {
/* Isolated code for support of grid */
}
请注意第一个块中的 not
操作符。它正在检查那些不支持网格的浏览器,以便将某些样式应用于这些浏览器。这种方法被认为是不好的做法的原因是必须考虑对 @supports 本身的浏览器支持!。这就是让它变得如此棘手的原因。
以逻辑分离的 @supports
块的方式编写代码非常吸引人,因为这样每次都从头开始,不需要覆盖之前的值,也不需要处理那些逻辑上的难题。但是,让我们回到之前考虑过的相同 iOS 情况……@supports
在 iOS 版本 9 中发布(正好在弹性盒子在 iOS 7 中发布和网格在 iOS 10.2 中发布之间)。这意味着任何使用 not
操作符检查 (display: grid) {}
支持的 @supports
块中的弹性盒子回退代码在 iOS 7 或 8 中都不会起作用,这意味着回退现在需要一个回退来在其他情况下起作用的浏览器中起作用。Whew!
使用 @supports
的主要原因是,根据功能支持,考虑非常不同的实现方式,如果代码块是分开的,那么在这些实现之间推理和区分会更容易。
我们可能会达到一个能够使用这种互斥块而无需担心的时候。说到这个……
@supports 随着时间的推移可能会变得更加有用。
一旦 @supports
在所有需要支持的浏览器中得到支持,那么就可以开始更积极地使用它,而无需考虑 @supports
本身是否受支持的复杂问题。以下是关于它的支持网格
这些浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器从该版本开始支持该功能。
台式机
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
28 | 22 | 否 | 12 | 9 |
移动设备/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 4.4 | 9.0-9.2 |
基本上,IE 11 和任何仍然使用 iOS 8 的 iOS 设备都是痛点。如果你的需求已经超过了这些,那么你可以更自由地使用 @supports
。
讽刺的是,还没有出现太多具有明确 @supports
用例的 CSS 功能,但有一些!显然,可以测试一些新的花哨功能,比如 Houdini
在我的婚礼网站上使用它来检查 Houdini 支持 🎩🐰
— Sam Richard (@Snugug) 2019 年 2 月 6 日
(我不确定在 @supports
块中放入什么来做到这一点。还有其他人做过吗?)
当 @supports 没有做任何有用的事情时
我见过很多 @supports
的使用,最终的结果和不使用它完全一样。例如……
@supports (transform: rotate(5deg)) {
.avatar {
transform: rotate(5deg);
}
}
在某种程度上,这在逻辑上是完全合理的。如果支持变换,就使用它们。但是,如果在不支持的情况下没有发生任何不同的事情,那么它是没有必要的。在这种情况下,变换可以在没有 @supports
块的情况下失败,结果是一样的。
这是 另一个示例。
有一些浏览器扩展可以用来玩 @supports
有两个!
- 特性查询管理器,由 Ire Aderinokun 编写
- CSS 特性切换扩展,由 Keith Clark 编写
它们都是围绕这样一个想法设计的,即我们可以在 CSS 中编写 @supports
块,然后像在支持或不支持该功能的浏览器中查看代码的渲染一样,打开或关闭它们。
这是一个使用弹性盒子回退的网格场景的 Keith 工具的视频
Ire 的工具(她在文章 创建特性查询管理器开发工具扩展 中写了关于这个工具的文章)采用了 slightly different approach,它显示了在 CSS 中实际编写的特性查询,并提供切换按钮来打开和关闭它们。我认为它不能通过 iframe 工作,所以我打开了 调试模式 来在 CodePen 上使用它。
更多现实世界中@supports 的用例
这是来自 Erik Vorhes 的一个。他正在 这里为一些自定义复选框和单选按钮设置样式,但将所有内容都包装在一个 @supports
块中。除非块通过支持检查,否则不会应用任何样式。
@supports (transform: rotate(1turn)) and (opacity: 0) {
/* all the styling for Erik's custom checkboxes and radio buttons */
}
以下是我遇到的其他几个例子
- Joe Wright 和 Tiago Nunes 提到了将它用于
position: sticky;
。我希望能看到这里的演示!就像你在position: sticky;
上,但必须做一些不同的事情,而不是让它在不支持的浏览器中失败。 - Keith Grant 和 Matthias Ott 提到了将它用于
object-fit: contain;
。Matthias 有一个演示,其中使用定位技巧来使图像填充容器,当该属性可用时,它会通过该属性变得更容易和更好。 - Ryan Filler 提到 将它用于
mix-blend-mode
。他的示例在元素上设置了更高的不透明度,但是如果支持mix-blend-mode
,它会使用更低的透明度和该属性,这会导致通过自身元素看到的效果。
.thing {
opacity: 0.5;
}
@supports (mix-blend-mode: multiply) {
.thing {
mix-blend-mode: multiply;
opacity: 0.75;
}
}
backdrop-filter
属性。他说,“当它被支持时,背景颜色的不透明度通常需要一些微调。”@supports (-ms-ime-align:auto) { }
。clip-path
,因为调整元素的大小或填充将适应剪裁不可用时的状况。initial-letter
CSS 属性,它对于下降帽非常棒,但与其他属性一起使用,如果你不支持 initial-letter
(或者有完全不同的备用方案),你可能不想完全应用这些属性。这是来自 Nick Colley 的一个额外内容,不是 @supports
,而是 @media
!精神是一样的。它可以防止这样的触摸设备上的“卡住”悬停状态
@media (hover: hover) {
a:hover {
background: yellow;
}
}
@supports 中的逻辑
基本
@supports (initial-letter: 4) {
}
不
@supports not (initial-letter: 4) {
}
和
@supports (initial-letter: 4) and (transform: scale(2)) {
}
或
@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {
}
组合
@supports ((display: -webkit-flex) or
(display: -moz-flex) or
(display: flex)) and (-webkit-appearance: caret) {
}
JavaScript 变体
JavaScript 有一个 API 可以实现这一点。要测试它是否存在……
if (window.CSS && window.CSS.supports) {
// Apparently old Opera had a weird implementation, so you could also do:
// !!((window.CSS && window.CSS.supports) || window.supportsCSS || false)
}
要使用它,要么将属性作为第一个参数传递给它,将值作为另一个参数传递给它
const supportsGrid = CSS.supports("display", "grid");
……或者将所有内容都放在一个字符串中,反映 CSS 语法
const supportsGrid = CSS.supports("(display: grid)");
选择器测试
在撰写本文时,只有 Firefox 支持这种测试(在实验性标志后面),但有一种方法可以使用 @supports
来测试对选择器的支持。 MDN 的演示
@supports selector(A > B) {
}
你呢?
当然,我们希望看到评论中 @supports
用例的 Pens。所以分享吧!
一个使用 Houdini 的示例是:background-image: paint(checkerboard);
我像这样模仿
@supports selector()
我想知道 PostCSS 是否将实际语法更改为类似的东西……
感谢这份精彩的总结。关于 Houdini 和
@supports
,这里是我在创建 https://css-houdini.rocks 时做的一些例子在绘制倾斜背景时添加
padding
(https://css-houdini.rocks/slanted-backgrounds)在绘制自定义复选框时添加
appearance: none
(https://css-houdini.rocks/checkboxes)在绘制不规则背景时移除备用背景(应用为
border-image
+fill
)(https://css-houdini.rocks/rough-boxes)在绘制圆角时重置
border-radius
,也称为椭圆形 (https://css-houdini.rocks/smooth-corners)在绘制不规则蒙版(作为
border-image
+outset
绘制)时重置overflow
(https://css-houdini.rocks/irregular-grid)所有演示都有代码示例 :)
我认为某些版本的 IE 也支持“支持”。
但它们不支持。Edge 支持,IE 不支持。
这是我推文中提到的下降帽示例的演示:https://codepen.io/stacy/pen/mORmWo
关于“Joe Wright 和 Tiago Nunes 提到了将它用于 position: sticky”…
主要原因是当可能粘性的元素必须是(并且始终是)包含块以用于绝对定位的后代时。你不能让它只是回退到 position: static,因为后代会失去那个包含块。所以你需要定位它,最有可能使用相对定位,但盒子偏移值,比如 top: 50px,并不能从粘性定位很好地回退到相对定位方案。
所以我通常会有……
……这完全是除了人们部署的 js 复杂粘性/固定 polyfilling 以外。
我一直在大量使用
@supports
来扩展 CSS,以使用需要 JavaScript 插件支持的样式。事实证明,你可以在 CSS 中存储并成功解析的内容与需要传递给与样式相关的插件的信息之间存在相当大的重叠。对我来说,一个常见的例子可能是这样的
你可以从 Github 上的 这个自述文件 中了解更多关于这种使用
@supports
的方法,或者在 CodePen 上查看更多示例这篇文章很棒!我在三年多前发布了一篇关于相同主题的文章,当时我得出了相同的结论——等待对 @supports 的支持。
https://pawelgrzybek.com/native-feature-detection-with-csssupports-api/
这是一个使用
@supports
检测你当前使用的浏览器中 CSS 功能的示例Safari,包括移动版和桌面版,在检测对 css 变量的支持时存在问题。我不得不使用这个 gist 的变体才能让 Apple 用户访问我的婚礼网站 :-/
https://gist.github.com/wesbos/8b9a22adc1f60336a699
嗨,内容很棒!
所以我在想一个场景
假设我需要支持 IE 10,不能使用 display flex 或 grid。
如果我已经必须支持旧浏览器(使用浮动或内联块显示)并编写一个在所有浏览器中都起作用的代码,为什么我还要在 supports 括号内编写任何内容?
如果我们自定义了
<select>
,会怎么样?在这种情况下,您可能希望删除右侧的默认箭头,并添加自己的 SVG 图标。但您最好确保它能正常工作,并且 IE 不会同时显示两个箭头。select {
border: 1px solid purple;
background: pink;
...
}
@supports (-webkit-appearance: none) or (-moz-appearance: none) {
select {
-webkit-appearance: none;
-moz-appearance: none;
padding-right: 30px;
background: url("arrow.svg");
}
}
Anton Shchukin 是在回复我吗?
无论如何,在这种情况下,您只会在它支持的情况下进行样式设置吗?
我认为最好的解决方案是在默认浏览器箭头之上插入图像,这样它就能在所有浏览器中正常显示。
它始终居中并位于右侧,因此您可以通过背景插入,甚至可以使用伪元素 after 或者使用 select 上包裹它的 div 的绝对定位。