CSS 中有一个你可能还不知道的工具。它很强大。它已经存在了一段时间了。而且它很可能会成为你最喜欢的 CSS 新功能之一。
瞧,@supports
规则。也称为 特性查询。
使用 @supports
,你可以在 CSS 中编写一个小型测试,以查看某个“特性”(CSS 属性或值)是否受支持,并根据答案应用一个代码块(或不应用)。例如
@supports (display: grid) {
// code that will only run if CSS Grid is supported by the browser
}
如果浏览器理解 display: grid
,那么括号内的所有样式都将被应用。否则所有样式都将被跳过。
现在,关于特性查询的用途似乎存在一些困惑。这不是一种外部验证,用于分析浏览器是否正确实现了 CSS 属性。如果你正在寻找这样的工具,请在其他地方寻找。特性查询要求浏览器自我报告某个 CSS 属性/值是否受支持,并使用答案来决定是否应用一个 CSS 代码块。如果浏览器错误地或不完整地实现了某个特性,@supports
将无法帮助你。如果浏览器错误地报告了它支持的 CSS,@supports
也无法帮助你。它不是让浏览器错误消失的魔法棒。
尽管如此,我发现 @supports
非常有用。@supports
规则多次让我比没有它时更早地使用了新的 CSS。
多年来,开发人员使用 Modernizr 来做特性查询所做的事情——但 Modernizr 需要 JavaScript。虽然脚本可能很小,但使用 Modernizr 构建的 CSS 需要下载 JavaScript 文件,执行 JavaScript 文件,并在应用 CSS 之前完成执行。涉及 JavaScript 始终比只使用 CSS 慢。需要 JavaScript 会导致出现故障的可能性——如果 JavaScript 没有执行会发生什么?此外,Modernizr 需要额外的复杂性,许多项目根本无法处理。特性查询更快、更强大且更容易使用。
你可能注意到特性查询的语法非常像媒体查询。我认为它们是表兄弟。
@supports (display: grid) {
main {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
}
现在,大多数时候,你的 CSS 中不需要这样的测试。例如,你可以编写以下代码,而无需测试支持
aside {
border: 1px solid black;
border-radius: 1em;
}
如果浏览器理解 border-radius
,那么它将在 aside
盒子上设置圆角。如果它不理解,它将跳过这一行代码并继续执行,留下盒子的边缘为方形。没有理由运行测试或使用特性查询。这就是 CSS 的工作方式。这是在 构建可靠的、渐进增强 CSS 中的一个基本原则。浏览器只是跳过它们不理解的代码,而不会抛出错误。

border-radius: 1em
的结果。然而,Internet Explorer 6、7 和 8 不会圆角,你将在左侧看到结果。请查看 codepen.io/jensimmons/pen/EydmkK 上的这个示例。你不需要为此使用特性查询。
那么,你什么时候想使用 @supports
呢?特性查询是一种将 CSS 声明捆绑在一起的工具,以便它们在特定条件下作为一个组运行。当你想要应用旧的和新的 CSS 的混合,但仅当新的 CSS 受支持时才应用时,请使用特性查询。
让我们看一个使用首字母属性的示例。这个新的属性 initial-letter
告诉浏览器使相关的元素变大——就像首字母大写一样。在这里,段落中第一个单词的第一个字母被设置为文本四行的大小。太棒了。哦,但我也想让这个字母变粗体,并在其右侧添加一些边距,嘿,让我们把它变成一种漂亮的橙色。酷。
p::first-letter {
-webkit-initial-letter: 4;
initial-letter: 4;
color: #FE742F;
font-weight: bold;
margin-right: 0.5em;
}

现在让我们看看在所有其他浏览器中会发生什么…

好吧,这是不可接受的。我们不想更改字母的颜色,或者添加边距,或者使它变粗体,除非它也要通过首字母属性变大。我们需要一种方法来测试浏览器是否理解 initial-letter
,并且仅在它理解的情况下才应用颜色、粗细和边距的更改。特性查询就应运而生了。
@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {
p::first-letter {
-webkit-initial-letter: 4;
initial-letter: 4;
color: #FE742F;
font-weight: bold;
margin-right: 0.5em;
}
}
请注意,你需要测试包含属性和值的完整字符串。这最初让我感到困惑。为什么我要测试 initial-letter: 4
?值 4 重要吗?如果我输入 17 会怎么样?它需要与我在代码中进一步使用的值匹配吗?
@supports
规则测试一个包含属性和值的字符串,因为有时需要测试的是属性,有时需要测试的是值。对于 initial-letter
示例,你为值输入什么并不重要。但请考虑 @supports (display: grid)
,你会看到需要同时测试两者。每个浏览器都理解 display
。只有实验性的浏览器理解 display: grid
(目前)。
回到我们的示例:目前 initial-letter
仅在 Safari 9 中受支持,并且需要前缀。因此,我写了前缀,确保也包含无前缀版本,并且我写了测试来查找其中一个或另一个。是的,你可以在特性查询中使用 or
、and
和 not
语句。
以下是新的结果。理解 initial-letter
的浏览器显示了一个巨大的粗体橙色首字母。其他浏览器则表现得好像首字母不存在一样——就像我等待更多浏览器支持这个特性后再使用它一样。(顺便说一句,我们目前正在 Firefox 中实现首字母。)

组织你的代码
现在,你可能很想使用这个工具将你的代码干净地分成两个分支。“嘿,浏览器,如果你理解视窗单位,就执行这个,如果你不理解,就执行另一个”。感觉很好,很整洁。
@supports (height: 100vh) {
// my layout that uses viewport height
}
@supports not (height: 100vh) {
// the alternative layout for older browsers
}
// WE MIGHT WISH. BUT THIS IS BAD CODE.
这不是一个好主意——至少现在不是。你看到了问题吗?
好吧,并非所有浏览器都支持特性查询。而且不理解 @supports
的浏览器将跳过这两个代码块。这可能很糟糕。
这意味着我们必须等到 100% 的浏览器支持特性查询才能使用它们吗?不。我们可以,我们应该现在就使用特性查询。只是不要像最后一个示例那样编写代码。
我们应该如何正确地做到这一点?好吧,就像我们使用媒体查询之前还没有 100% 支持一样。实际上,在过渡期使用特性查询比使用媒体查询更容易。你只需要聪明一点。
你需要在知道最旧的浏览器不支持特性查询或你正在测试的特性的情况下构建你的代码。我将向你展示如何做到这一点。
(当然,在遥远的未来,一旦 100% 的浏览器都支持特性查询,我们就可以更频繁地使用 @supports not
并以这种方式组织我们的代码。但要实现这一点还需要很多年。)
特性查询的支持
那么,特性查询从哪个版本开始支持呢?
好吧,@supports
从 2013 年年中开始就在 Firefox、Chrome 和 Opera 中运行。它在 Edge 的每个版本中也都能运行。Safari 在 2015 年秋季的 Safari 9 中发布了它。任何版本的 Internet Explorer、Opera Mini、Blackberry Browser 或 UC Browser 均不支持特性查询。

你可能认为 Internet Explorer 不支持特性查询是一个大问题。实际上,通常不是这样。我将稍后向你解释原因。我认为最大的障碍是 Safari 8。我们需要密切关注那里发生的事情。
让我们看另一个示例。假设我们有一些我们想要应用的布局代码,它需要使用 object-fit: cover
才能正常工作。对于不理解 object-fit
的浏览器,我们想要应用不同的布局 CSS。

所以让我们编写
div {
width: 300px;
background: yellow;
// some complex code for a fallback layout
}
@supports (object-fit: cover) {
img {
object-fit: cover;
}
div {
width: auto;
background: green;
// some other complex code for the fancy new layout
}
}
那么会发生什么?特性查询要么支持,要么不支持,并且新特性 object-fit: cover
要么支持,要么不支持。将它们组合起来,我们得到四种可能性
特性查询支持? | 特性支持? | 会发生什么? | 这是我们想要的结果吗? |
---|---|---|---|
支持特性查询 | 支持所讨论的特性 | ||
支持特性查询 | 不支持所讨论的特性 | ||
不支持特性查询 | 不支持所讨论的特性 | ||
不支持特性查询 | 支持所讨论的特性 |
情况 1:支持特性查询且支持所讨论特性的浏览器
Firefox、Chrome、Opera 和 Safari 9 都支持 object-fit
并且支持 @supports
,因此此测试将正常运行,并且将应用此代码块中的代码。我们的图像将使用 object-fit: cover
进行裁剪,并且我们的 div
背景将变为绿色。
情况 2:支持特性查询但不支持所讨论特性的浏览器
Edge 不支持 object-fit
,但它支持 @supports
,因此此测试将运行并失败,从而阻止应用此代码块。图像将不会应用 object-fit
,并且 div
将具有黄色背景。
这就是我们想要的。
情况 3:不支持特性查询也不支持所讨论特性的浏览器
这就是我们的老对手 Internet Explorer 出现的地方。IE 不理解 @supports
并且它不理解 object-fit
。你可能认为这意味着我们无法使用特性查询——但这并不正确。
想想我们想要的结果。我们希望 IE 跳过整个代码块。而这正是会发生的事情。为什么?因为当它到达 @supports
时,它不识别语法,并且跳到结尾。
它可能“因为错误的原因”跳过了代码——它跳过了代码是因为它不理解 @supports
,而不是因为它不理解 object-fit
——但这有什么关系!我们仍然得到了我们想要的确切结果。
对于 Blackberry Browser 和 UC Browser for Android 也是如此。它们不理解 object-fit
,也不理解 @supports
,所以一切都很好。结果非常棒。
底线——只要你不支持特性查询的浏览器也不支持你正在测试的特性,那么你随时可以在不支持特性查询的浏览器中使用特性查询,这都没有问题。
仔细思考代码的逻辑。问问自己,当浏览器跳过这段代码时会发生什么?如果这就是你想要的结果,那么一切都已就绪。
情况 4:支持功能查询但支持该功能的浏览器
问题在于这第四种组合——当功能查询提出的测试没有运行时,但浏览器确实支持该功能并且应该运行该代码。
例如,object-fit
受 Safari 7.1(在 Mac 上)和 8(Mac 和 iOS)支持——但这两个浏览器都不支持功能查询。Opera Mini 也是如此——它将支持 object-fit
但不支持 @supports
。
会发生什么?这些浏览器会进入这段代码块,而不是使用代码将 object-fit:cover
应用于图像并将 div
的背景颜色改为绿色,而是跳过整个代码块,使背景颜色保持黄色。
这不是我们真正想要的。
特性查询支持? | 特性支持? | 会发生什么? | 这是我们想要的结果吗? |
---|---|---|---|
支持特性查询 | 支持所讨论的特性 | CSS 应用 | 是 |
支持特性查询 | 不支持所讨论的特性 | CSS 未应用 | 是 |
不支持特性查询 | 不支持所讨论的特性 | CSS 未应用 | 是 |
不支持特性查询 | 支持所讨论的特性 | CSS 未应用 | 否,可能不会。 |
当然,这取决于具体的用例。也许这是我们可以接受的结果。旧浏览器获得了为旧浏览器设计的体验。网页仍然可以工作。
但大多数情况下,我们希望浏览器能够使用它支持的任何功能。这就是为什么在功能查询方面,Safari 8 可能是最大的问题,而不是 Internet Explorer。Safari 8 支持许多较新的属性——例如 Flexbox。你可能不想阻止 Safari 8 使用这些属性。这就是为什么我很少在 Flexbox 中使用 @supports
,或者当我使用它时,我至少在我的代码中写了三个分支,其中一个带有 not
。(这很快就会变得复杂,所以我甚至不会在这里解释。)
如果你使用的是在旧浏览器中比功能查询支持更好的功能,那么在编写代码时要考虑所有组合。确保不要阻止浏览器获得你希望它们获得的东西。
与此同时,使用最新 CSS 功能(例如 CSS Grid 和 Initial Letter)很容易使用 @supports
。任何浏览器都不会在不支持功能查询的情况下支持 CSS Grid。我们不必担心使用最新功能时出现的第四个有问题的组合,这使得功能查询在我们前进的道路上变得非常有用。
这意味着,虽然 IE11 很可能在未来几年内继续存在,但我们可以将功能查询广泛地应用于 CSS 的最新进展。
最佳实践
现在我们意识到为什么不能这样写代码
@supports not (display: grid) {
// code for older browsers
// DO NOT COPY THIS EXAMPLE
}
@supports (display: grid) {
// code for newer browsers
// DID I SAY THIS IS REALLY BAD?
}
如果我们这样做,我们将阻止旧浏览器获得它们所需的代码。
相反,请像这样构建代码
// fallback code for older browsers
@supports (display: grid) {
// code for newer browsers
// including overrides of the code above, if needed
}
这正是我们在支持旧版 IE 时使用媒体查询所采用的策略。这种策略催生了“移动优先”这一说法。
我预计 CSS Grid 将在 2017 年登陆浏览器,并且我相信我们在实现未来布局时会经常使用功能查询。这比使用 JavaScript 要轻松得多,速度也快得多。@supports
将使我们能够为支持 CSS Grid 的浏览器做一些有趣而复杂的事情,同时为不支持的浏览器提供布局选项。
功能查询从 2013 年中期开始存在。随着 Safari 10 的即将发布,我认为现在是时候将 @supports
添加到我们的工具箱中了。
关于 Jen Simmons
Jen Simmons 是 Mozilla 的设计师和开发者倡导者,专门从事网页上的 CSS 和布局设计。她是 CSS 工作组的成员,并在许多会议上发表演讲,包括 An Event Apart、SXSW、Fluent、Smashing Conf 等。
16 条评论