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 |
浏览器兼容性
加载中…