使用 CSS 嵌套
CSS 嵌套模块允许你编写样式表,使其更易于阅读、更模块化且更易于维护。由于你无需不断重复选择器,文件大小也可以减小。
CSS 嵌套与 Sass 等 CSS 预处理器不同,它是由浏览器解析而不是由 CSS 预处理器预编译的。此外,在 CSS 嵌套中,& 嵌套选择器的特异性类似于 :is() 函数;它是使用关联选择器列表中最高的特异性计算的。
本指南展示了在 CSS 中安排嵌套的不同方式。
子选择器
你可以使用 CSS 嵌套为父级创建子选择器,这些子选择器又可以用于定位特定父级的子元素。这可以在使用或不使用 & 嵌套选择器的情况下完成。
在某些情况下,使用 & 嵌套选择器是必要或有帮助的
/* Without nesting selector */
.parent {
/* parent styles */
.child {
/* child of parent styles */
}
}
/* With nesting selector */
.parent {
/* parent styles */
& .child {
/* child of parent styles */
}
}
/* the browser will parse both of these as */
.parent {
/* parent styles */
}
.parent .child {
/* child of parent styles */
}
示例
在这些示例中,一个不带 & 嵌套选择器,一个带 & 嵌套选择器,<label> 内部的 <input> 的样式与 <label> 的同级 <input> 的样式不同。
不带嵌套选择器
HTML
<form>
<label for="name">Name:
<input type="text" id="name" />
</label>
<label for="email">email:</label>
<input type="text" id="email" />
</form>
CSS
input {
/* styles for input not in a label */
border: tomato 2px solid;
}
label {
/* styles for label */
font-family: system-ui;
font-size: 1.25rem;
input {
/* styles for input in a label */
border: blue 2px dashed;
}
}
结果
带嵌套选择器
CSS
input {
/* styles for input not in a label */
border: tomato 2px solid;
}
label {
/* styles for label */
font-family: system-ui;
font-size: 1.25rem;
& input {
/* styles for input in a label */
border: blue 2px dashed;
}
}
结果
组合器
CSS 组合器也可以在使用或不使用 & 嵌套选择器的情况下使用。
示例
嵌套同级组合器
在此示例中,每个 <h2> 之后的第一个段落都使用 CSS 嵌套和相邻兄弟选择器(+)进行定位。
HTML
<h2>Heading</h2>
<p>This is the first paragraph.</p>
<p>This is the second paragraph.</p>
CSS
h2 {
color: tomato;
+ p {
color: white;
background-color: black;
}
}
/* this code can also be written with the & nesting selector */
/*
h2 {
color: tomato;
& + p {
color: white;
background-color: black;
}
}
*/
结果
复合选择器
在嵌套 CSS 中使用复合选择器时,你必须使用 & 嵌套选择器。这是因为浏览器会自动在不使用 & 嵌套选择器的选择器之间添加空格。
为了使用 class="a b" 定位元素,需要 & 嵌套选择器,否则空格会破坏复合选择器。
.a {
/* styles for element with class="a" */
.b {
/* styles for element with class="b" which is a descendant of class="a" */
}
&.b {
/* styles for element with class="a b" */
}
}
/* the browser parses this as */
.a {
/* styles for element with class="a" */
}
.a .b {
/* styles for element with class="b" which is a descendant of class="a" */
}
.a.b {
/* styles for element with class="a b" */
}
示例
嵌套和复合选择器
在此示例中,使用 & 嵌套选择器创建复合选择器来设置具有多个类的元素的样式。
HTML
<div class="notices">
<div class="notice">
<h2 class="notice-heading">Notice</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</div>
<div class="notice warning">
<h2 class="warning-heading">Warning</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</div>
<div class="notice success">
<h2 class="success-heading">Success</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</div>
</div>
CSS
.notices 的样式使用弹性布局创建列。
.notices {
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 90%;
margin: auto;
}
在下面的 CSS 代码中,嵌套用于创建带有 & 和不带 & 的复合选择器。顶层选择器定义了具有 class="notice" 的元素的基本样式。然后,使用 & 嵌套选择器为具有 class="notice warning" 或 class="notice success" 的元素创建复合选择器。此外,在选择器 .notice .notice-heading::before 中可以看到不显式使用 & 创建复合选择器的嵌套用法。
.notice {
width: 90%;
justify-content: center;
border-radius: 1rem;
border: black solid 2px;
background-color: #ffc107;
color: black;
padding: 1rem;
.notice-heading::before {
/* equivalent to `.notice .notice-heading::before` */
content: "ℹ︎ ";
}
&.warning {
/* equivalent to `.notice.warning` */
background-color: #d81b60;
border-color: #d81b60;
color: white;
.warning-heading::before {
/* equivalent to `.notice.warning .warning-heading::before` */
content: "! ";
}
}
&.success {
/* equivalent to `.notice.success` */
background-color: #004d40;
border-color: #004d40;
color: white;
.success-heading::before {
/* equivalent to `.notice.success .success-heading::before` */
content: "✓ ";
}
}
}
结果
附加嵌套选择器
& 嵌套选择器也可以附加到嵌套选择器,这会反转上下文。
当子元素的样式因父元素被赋予不同的类而改变时,这可能很有用。
<div>
<span class="foo">text</span>
</div>
而不是
<div class="bar">
<span class="foo">text</span>
</div>
.foo {
/* .foo styles */
.bar & {
/* .bar .foo styles */
}
}
示例
附加嵌套选择器
在此示例中,有 3 张卡片,其中一张是特色卡片。除了特色卡片的标题颜色不同之外,所有卡片都完全相同。通过附加 & 嵌套选择器,.featured h2 的样式可以嵌套在 h2 的样式中。
HTML
<div class="wrapper">
<article class="card">
<h2>Card 1</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.</p>
</article>
<article class="card featured">
<h2>Card 2</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.</p>
</article>
<article class="card">
<h2>Card 3</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.</p>
</article>
</div>
CSS
.wrapper {
display: flex;
flex-direction: row;
gap: 0.25rem;
font-family: system-ui;
}
在下面的 CSS 中,我们正在为 .card 和 .card h2 创建样式。然后,在 h2 样式块中,我们嵌套了附加 & 嵌套选择器的 .featured 类,这为 .card :is(.featured h2) 创建了样式,这等同于 :is(.card h2):is(.featured h2)。
.card {
padding: 0.5rem;
border: 1px solid black;
border-radius: 0.5rem;
& h2 {
/* equivalent to `.card h2` */
color: slateblue;
.featured & {
/* equivalent to `:is(.card h2):is(.featured h2)` */
color: tomato;
}
}
}
结果
嵌套声明规则
嵌套声明规则是 CSS 规则按照它们在 CSS 文档中编写的顺序进行解析。
使用以下 CSS
.foo {
background-color: silver;
@media screen {
color: tomato;
}
color: black;
}
background-color 首先被解析并设置为 silver,然后评估 @media 规则,最后是 color。
CSSOM 以以下方式解析 CSS
↳ CSSStyleRule
.style
- background-color: silver
↳ CSSMediaRule
↳ CSSNestedDeclarations
.style (CSSStyleDeclaration, 1) =
- color: tomato
↳ CSSNestedDeclarations
.style (CSSStyleDeclaration, 1) =
- color: black
请注意,为了保持解析顺序,所有嵌套之前的规则都作为顶层 CSSRules 处理,而嵌套之后的任何顶层规则都表示为 CSSNestedDeclarations。这就是为什么 color-black 在嵌套声明中,即使它在原始文档中是一个顶层声明。
注意: 对此规则的支持已通过 CSSNestedDeclarations 添加。 不支持此接口的浏览器可能会以错误的顺序解析嵌套规则。
串联(不可能)
在 Sass 等 CSS 预处理器中,可以使用嵌套连接字符串以创建新类。这在 BEM 等 CSS 方法中很常见。
.component {
&__child-element {
}
}
/* In Sass this becomes */
.component__child-element {
}
在复合选择器中,类型选择器必须在最前面。编写 &Element(一个类型选择器)会使 CSS 选择器和整个选择器块无效。由于类型选择器必须在最前面,因此复合选择器必须写成 Element&。
.my-class {
element& {
}
}
/* the browser parses this to become a compound selector */
.my-class {
}
element.my-class {
}
无效的嵌套样式规则
如果嵌套的 CSS 规则无效,则所有包含的样式都将被忽略。这不会影响父规则或前面的规则。
在以下示例中,存在一个无效的选择器(% 不是选择器的有效字符)。包含此选择器的规则将被忽略,但后续的有效规则不会。
.parent {
/* .parent styles these work fine */
& %invalid {
/* %invalid styles all of which are ignored */
}
& .valid {
/* .parent .valid styles these work fine */
}
}