if()
语法
/* Single <if-test> */
if(style(--scheme: dark): #eeeeee;)
if(media(print): black;)
if(media(width > 700px): 0 auto;)
if(supports(color: lch(7.1% 60.23 300.16)): lch(7.1% 60.23 300.16);)
/* <if-test> with else */
if(style(--size: "2xl"): 1em; else: 0.25em;)
if(media(print): white; else: black;)
if(media(width < 700px): 0 auto; else: 20px auto)
if(
supports(color: lch(7.1% 60.23 300.16)): lch(7.1% 60.23 300.16);
else: #03045e;
)
if(
supports(color: lch(75% 0 0)): 3px solid lch(75% 0 0);
else: 3px solid silver;
)
/* Multiple <if-test>s */
if(
style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971);
else: none;
)
/* <if-test> within a shorthand */
3px yellow if(
style(--color: green): dashed;
style(--color: yellow): inset;
else: solid;
)
参数
参数是一个以分号分隔的 <if-branch>
列表。每个 <if-branch>
是一个 <if-condition>
,后面跟着一个冒号和一个 <value>
。
<if-branch> = <if-condition> : <value>;
返回值
一个值或保证无效值。
描述
CSS if()
函数为 CSS 属性值提供了条件逻辑,其工作方式类似于 JavaScript 的 if...else
语句。
if()
函数可以在任何属性的值中使用,并且可以包含零个或多个以分号分隔的 <if-condition>
。每个 <if-condition>
要么是一个 <if-test> : <value>
对,要么是一个 else : <value>
对。最后一个 <if-condition>
后面的分号是可选的。
返回值按如下方式计算
<if-condition>
表达式按照它们在函数中出现的顺序进行评估。- 第一个评估为
true
的<if-condition>
将返回其关联的<value>
。 - 如果没有
<if-condition>
评估为true
,则函数返回一个 <guaranteed-invalid>。如果在具有回退值的值语句中使用if()
函数(例如自定义属性或anchor()
函数),这会表现为无效或false
。
例如
div {
background-image: if(
style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971);
else: none;
);
}
在这个例子中,我们根据 --scheme
自定义属性是否设置为 ice
或 fire
,为 <div>
元素的 background-image
设置不同的 linear-gradient()
。如果 --scheme
不存在,或者存在但设置为任何其他值,则 else
值生效,background-image
属性将被设置为 none
。
备注: 每个条件必须用冒号与其关联的值分隔开,并且每个 <if-condition> : <value>
对必须用分号分隔开。最后一个 <if-condition> : <value>
对的分号是可选的。
警告: if
和左括号((
)之间不能有空格。否则,整个声明将无效。
如果单个 <if-condition>
或 <value>
无效,它不会使整个 if()
函数无效;相反,解析器会继续处理下一个 <if-condition> : <value>
对。如果没有一个 <if-condition>
或 <value>
是有效的,函数将返回保证无效值。
else : <value>
对的频率和位置
你可以在一个 if()
函数中包含多个 else : <value>
对,位置不限。然而,在大多数情况下,在分号分隔列表的末尾使用单个 else : <value>
对来提供默认值,如果没有任何一个 <if-test>
评估为 true,则始终返回该默认值。
如果你在任何 <if-test> : <value>
对之前包含一个 else : <value>
对,其后的条件将不会被评估,因为 else
总是评估为 true
。因此,下面的 if()
总是返回 none
,而两个 <if-test> : <value>
对永远不会被评估。
div {
background-image: if(
else: none;
style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971)
);
}
调试一个行为不符合预期的值时,你可能希望将 else : <value>
放在值列表末尾以外的位置。在下面的例子中,我们试图确定第一个 <if-test> : <value>
对是否正常工作。如果不是,else : <value>
对会返回一个 url("debug.png")
值,以显示一张图片,指示第一个 <if-test> : <value>
对需要修复。最后两个 <if-test> : <value>
对同样永远不会被评估。
div {
background-image: if(
style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
else: url("debug.png");
style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971);
else: none;
);
}
请注意,如果 if()
函数只包含一个 else : <value>
对,或者什么都不包含,它仍然是有效的。以下属性值都是有效的。
background-color: if(else: yellow);
background-image: if();
这些函数并没有什么用。包含它们是为了证明其有效性。在这种情况下,background-color
值将始终设置为 yellow
,而 background-image
将被设置为其初始值。你最好直接将 background-color
设置为 yellow
,将 background-image
设置为 initial
或 none
。
if-test 的类型
<if-test>
接受三种查询类型之一。本节将详细介绍每一种。
样式查询
一个样式查询 <if-test>
允许你测试某个元素上是否设置了特定的属性值,并因此为另一个属性应用一个值。我们之前已经看过几个样式查询的例子;让我们再看一个例子。
background-image: if(
style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
else: none;
);
如果在同一个元素上 --scheme
自定义属性被设置为 ice
,则返回提供的 linear-gradient()
值。如果不是,则返回 none
。
在 if()
语句中使用样式查询比 @container
查询有一个优势——你可以根据元素上是否设置了自定义属性来直接为该元素设置样式,而无需检查容器父元素上设置的样式。
你还可以在样式查询中使用 and
、or
或 not
逻辑。例如:
background-color: if(
style((--scheme: dark) or (--scheme: very-dark)): black;
);
background-color: if(
style((--scheme: dark) and (--contrast: hi)): black;
);
background-color: if(
not style(--scheme: light): black;
);
@container
查询确实有一些优势——使用 if()
样式查询一次只能设置单个属性值,而 @container
查询可以用于有条件地应用整套规则。这两种方法是互补的,各有不同的用途。
请注意,容器样式查询目前不支持常规的 CSS 属性,只支持 CSS 自定义属性。例如,以下代码将无法工作:
if(
background-color: if(style(color: white): black;);
)
媒体查询
一个媒体查询 <if-test>
可以用来根据媒体查询测试是否返回 true 来设置属性的值。
你可以使用媒体类型。例如,下面的 <if-test> : <value>
对在打印媒体上返回 white
,而 else
子句使得在非打印媒体上返回 #eeeeee
。
background-color: if(
media(print): white;
else: #eeeeee;
)
你也可以使用媒体特性——如果当前视口宽度小于 700px
,以下代码返回 0 auto
;否则返回 20px auto
。
margin: if(
media(width < 700px): 0 auto;
else: 20px auto;
)
当你需要根据媒体查询结果改变单个属性值时,这非常有用。
你还可以在媒体查询中使用 and
、or
或 not
逻辑。例如:
border-color: if(
media((width > 700px) and (width < 1000px)): blue;
);
border-color: if(
media((width < 500px) or (orientation: landscape)): blue;
);
background-color: if(
not media(width < 500px): blue;
else: red
);
当你想根据一个媒体查询设置多个声明或规则时,需要使用常规的 @media
结构。这两种方法是互补的,各有不同的用途。
特性查询
一个特性查询 <if-test>
可以用来根据浏览器是否支持某个特定的属性值来设置属性的值。
例如,如果支持 lch()
颜色,以下代码将返回一个 lch()
颜色;否则返回一个 rgb()
颜色。
color: if(
supports(color: lch(75% 0 0)): lch(75% 0 0);
else: rgb(185 185 185);
)
选择器支持查询也同样有效。如果浏览器支持 :buffering
伪类,以下代码将返回 1em
;否则返回 initial
。
margin-top: if(
supports(selector(:buffering)): 1em;
else: initial;
)
你还可以在特性查询中使用 and
、or
或 not
逻辑。例如:
margin-top: if(
supports((selector(:buffering)) and (color: blue)): 1em;
);
margin-top: if(
supports((selector(:buffering)) or (color: not-a-color)): 1em;
);
margin-top: if(
supports(not selector(:buffering)): 1em;
);
当你需要根据对某个特定值或独立属性的支持来改变单个属性值时,在 if()
语句中使用特性查询非常有用。当你想根据一个特性查询设置多个声明或规则时,常规的 @supports
结构更好。这两种方法是互补的,各有不同的用途。
提供回退值
if()
语句不会优雅降级;需要为不支持的浏览器提供明确的回退值。
例如,在这种情况下,我们为不支持 if()
的浏览器提供了一个静态的 padding
值。支持 if()
的浏览器会用第二个声明覆盖第一个声明,根据 --size: "2xl"
自定义属性是否设置来设置不同的内边距值。
padding: 1em;
padding: if(style(--size: "2xl"): 1em; else: 0.25em);
备注: 记住要包含 else
条件。在支持 if()
的浏览器中,如果没有包含 else
值且 --size
不等于 "2xl"
,padding 将被设置为 initial
。
完整值和部分值
if()
函数可以作为任何 CSS 属性的值,但它也可以用来决定属性值的一部分。例如,下面的代码根据是否支持 lch()
颜色,在 border
简写属性中设置了不同的 border-color
。
border: if(
supports(color: lch(75% 0 0)): 3px solid lch(75% 0 0);
else: 3px solid silver;
);
然而,我们也可以只用 if()
函数来决定 border-color
组件。
border: 3px solid
if(
supports(color: lch(75% 0 0)): lch(75% 0 0); else: silver;
);
嵌套 if() 函数
因为 if()
函数可以替代整个属性值或单个组件,所以可以在其他 if()
函数内部嵌套 if()
函数,也可以在 calc()
等其他函数内部嵌套。
例如,在这个声明中,我们使用 if()
根据各种条件来设置 color
属性值。我们有一个外部的 if()
函数,根据 --scheme
自定义属性是否设置为 ice
或 fire
来返回一个特定的值(如果两个条件都不满足,则返回 else
值 black
)。
然而,这两个 <value>
本身也是 if()
函数。这些内部的 if()
函数会在用户偏好深色配色方案时(通过 prefers-color-scheme
媒体查询确定)返回一个浅色值,否则返回一个深色值。
color: if(
style(--scheme: ice):
if(
media(prefers-color-scheme: dark): #caf0f8;
else: #03045e;
);
style(--scheme: fire):
if(
media(prefers-color-scheme: dark): #ffc971;
else: #621708;
);
else: black
);
在下一个例子中,我们将 width
属性设置为一个 calc()
函数,该函数从父元素宽度的百分比中减去 50px
。这个百分比由一个 if()
函数表示,该函数测试 --scheme: wide
自定义属性是否被设置。如果是,百分比为 70%
,所以外部函数解析为 calc(70% - 50px)
。如果不是,百分比为 50%
,所以外部函数解析为 calc(50% - 50px)
。
width: calc(if(
style(--scheme: wide): 70%;
else: 50%;
) - 50px);
正式语法
解析错误:意外的输入示例
if()
的基本用法
在这个例子中,我们将展示三种 <if-test>
类型的基本用法。
HTML
我们的 HTML 中有一个 <section>
元素,里面有两个 <article>
元素,包含 <h2>
标题。这个 <section>
在其 style
属性中设置了一个自定义属性——--show-apple:true
——我们稍后会用它来有条件地设置一个属性值。
<section style="--show-apple:true">
<article><h2>First article</h2></article>
<article><h2>Second article</h2></article>
</section>
CSS
在我们的 CSS 中,我们首先选择 <section>
元素,使用 flexbox 对其进行布局,并设置两个子 <article>
元素之间的 gap
。然后,我们使用一个带有 orientation
媒体查询 <if-test>
的 if()
函数来设置 flex-direction
属性的值,如果文档处于横向方向,则为 row
,如果处于纵向方向,则为 column
。这使得 article
元素在宽屏上并排布局,在窄屏上从上到下布局。
section {
display: flex;
gap: 16px;
flex-direction: if(
media(orientation: landscape): row;
else: column;
)
}
接下来,我们选择 <h2>
元素的 ::before
伪元素,将其 content
属性设置为一个苹果表情符号,但仅当 --show-apple: true
被设置时(我们之前在 HTML 中用内联 <style>
实现了这一点)。我们通过一个带有样式查询 <if-test>
的 if()
函数来实现这一点。
h2::before {
content: if(
style(--show-apple: true): "🍎 ";
);
}
最后,我们选择 <h2>
元素本身。我们使用一个特性查询 <if-test>
来测试浏览器是否支持 lch()
颜色,如果支持,则将 color
属性设置为一个 lch()
颜色,否则设置为一个等效的十六进制颜色。
h2 {
color: if(
supports(color: lch(29.57% 43.25 344.44)): lch(29.57% 43.25 344.44);
else: #792359;
)
}
结果
注意样式是如何应用的。通过使用浏览器的开发者工具修改渲染的演示,来测试前两个 if()
查询的条件样式。
- 移除
<section>
元素的style
属性,注意苹果表情符号是如何不再被渲染的。 - 将嵌入的
<iframe>
的height
属性改为1200px
。这将使方向从横向变为纵向。注意布局因此发生的变化。
用 if()
控制配色方案
这个演示展示了如何用 CSS if()
函数玩出真正的乐趣。其中,我们使用 if()
函数有条件地设置一些自定义属性的值,从而使我们能够控制整个配色方案!
HTML
我们的 HTML 包含一个 <article>
元素,里面有一些内容——一个顶级标题、几个 <p>
元素和一个 <aside>
。我们还包含了一个 <form>
,其中有一个 <select>
下拉菜单,用于选择配色方案。
<article>
<h1>Main heading</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit.
Quisque faucibus ex sapien vitae pellentesque sem placerat.
In id cursus mi pretium tellus duis convallis.
</p>
<aside>
<h2>An aside</h2>
<p>
Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas.
</p>
</aside>
<p>
Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut
hendrerit semper vel class aptent taciti sociosqu. Ad litora
torquent per conubia nostra inceptos himenaeos.
</p>
</article>
<form>
<label for="scheme">Choose color scheme:</label>
<select id="scheme">
<option value="">Default</option>
<option value="ice">Ice</option>
<option value="fire">Fire</option>
</select>
</form>
JavaScript
我们的 JavaScript 为 <select>
元素添加了一个 change
事件监听器。当选择了新值时,我们的脚本会将 <article>
元素的 class
属性设置为该值。
const articleElem = document.querySelector("article");
const selectElem = document.querySelector("select");
selectElem.addEventListener("change", () => {
articleElem.className = selectElem.value;
});
CSS
在我们的 CSS 中,我们给 <body>
元素设置了一个 700px
的 max-width
,并使用 auto
的 margin
值使其居中。然而,我们使用一个带有媒体查询 <if-test>
的 if()
函数,在 margin
简写属性中设置 margin-top
组件,如果视口宽度小于 700px
,则为 0
,如果更宽,则为 20px
。这意味着在宽屏上,我们在内容顶部会有一点外边距,但在窄屏上这会被移除,因为它看起来有点奇怪。
body {
max-width: 700px;
margin: if(
media(width < 700px): 0;
else: 20px;
) auto 0;
}
然后,我们将 --scheme
自定义属性设置为与 <article>
元素的 class
名称匹配。当在我们的 <select>
元素中选择新值时,我们的 JavaScript 会设置该 class。你将在下一个 CSS 块中看到该自定义元素值的重要性。
.ice {
--scheme: ice;
}
.fire {
--scheme: fire;
}
当我们将 CSS if()
函数与自定义属性结合使用时,我们可以看到它们的真正威力。在这里,我们使用 if()
函数,根据 --scheme
自定义属性的值,将我们的 --color1
和 --color2
自定义属性设置为不同的颜色值。然后,我们在 <article>
元素的 color
、border
和 background-image
属性,以及我们的 <aside>
元素的 color
和 background-color
属性中使用 --color1
和 --color2
的值。
我们通过自定义属性来控制整个配色方案,通过 if()
函数设置不同的值。
article {
padding: 20px;
--color1: if(
style(--scheme: ice): #03045e;
style(--scheme: fire): #621708;
else: black;
);
--color2: if(
style(--scheme: ice): #caf0f8;
style(--scheme: fire): #ffc971;
else: white;
);
color: var(--color1);
border: 3px solid var(--color1);
background-image: linear-gradient(
to left,
var(--color2),
white,
var(--color2)
);
}
aside {
color: var(--color2);
background-color: var(--color1);
padding: 20px;
}
最后,我们在另外几个地方使用了 if()
函数。
- 如果视口宽度大于
700px
,我们将<h1>
元素的font-size
设置为calc(3rem + 2vw)
,否则设置为3rem
。这意味着在宽屏上,字体大小会随着视口宽度的变化而动态更新,但在窄屏上保持不变。 - 我们根据
--scheme
自定义属性的值,为<h1>
元素的::before
伪类的content
设置了一个合适的表情符号。
h1 {
margin: 0;
font-size: if(
media(width > 700px): calc(3rem + 2vw);
else: 3rem;
);
}
h1::before {
content: if(
style(--scheme: ice): "❄️ ";
style(--scheme: fire): "🔥 ";
else: "";
);
}
结果
此演示渲染如下:
尝试选择不同的配色方案值,看看对外观和感觉的影响。
规范
规范 |
---|
CSS 值和单位模块 Level 5 # if-notation |
浏览器兼容性
加载中…