使用 CSS 嵌套

CSS 嵌套模块允许你编写样式表,使其更易于阅读、更模块化且更易于维护。由于你无需不断重复选择器,文件大小也可以减小。

CSS 嵌套与 Sass 等 CSS 预处理器不同,它是由浏览器解析而不是由 CSS 预处理器预编译的。此外,在 CSS 嵌套中,& 嵌套选择器的特异性类似于 :is() 函数;它是使用关联选择器列表中最高的特异性计算的。

本指南展示了在 CSS 中安排嵌套的不同方式。

子选择器

你可以使用 CSS 嵌套为父级创建子选择器,这些子选择器又可以用于定位特定父级的子元素。这可以在使用或不使用 & 嵌套选择器的情况下完成。

在某些情况下,使用 & 嵌套选择器是必要或有帮助的

  • 当选择器连接在一起时,例如使用复合选择器伪类
  • 为了向后兼容。
  • 作为视觉指示器以帮助提高可读性,当看到 & 嵌套选择器时,你就会知道正在使用 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
html
<form>
  <label for="name">Name:
    <input type="text" id="name" />
  </label>
  <label for="email">email:</label>
  <input type="text" id="email" />
</form>
CSS
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
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
html
<h2>Heading</h2>
<p>This is the first paragraph.</p>
<p>This is the second paragraph.</p>
CSS
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" 定位元素,需要 & 嵌套选择器,否则空格会破坏复合选择器。

css
.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
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 的样式使用弹性布局创建列。

css
.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 中可以看到不显式使用 & 创建复合选择器的嵌套用法。

css
.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: "✓ ";
    }
  }
}
结果

附加嵌套选择器

& 嵌套选择器也可以附加到嵌套选择器,这会反转上下文。

当子元素的样式因父元素被赋予不同的类而改变时,这可能很有用。

html
<div>
  <span class="foo">text</span>
</div>

而不是

html
<div class="bar">
  <span class="foo">text</span>
</div>
css
.foo {
  /* .foo styles */
  .bar & {
    /* .bar .foo styles */
  }
}

示例

附加嵌套选择器

在此示例中,有 3 张卡片,其中一张是特色卡片。除了特色卡片的标题颜色不同之外,所有卡片都完全相同。通过附加 & 嵌套选择器,.featured h2 的样式可以嵌套在 h2 的样式中。

HTML
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
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)

css
.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

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 方法中很常见。

css
.component {
  &__child-element {
  }
}
/* In Sass this becomes */
.component__child-element {
}

警告:这在 CSS 嵌套中是不可能的:当不使用组合器时,嵌套选择器被视为类型选择器。允许串联会破坏这一点。

复合选择器中,类型选择器必须在最前面。编写 &Element(一个类型选择器)会使 CSS 选择器和整个选择器块无效。由于类型选择器必须在最前面,因此复合选择器必须写成 Element&

css
.my-class {
  element& {
  }
}

/* the browser parses this to become a compound selector */
.my-class {
}
element.my-class {
}

无效的嵌套样式规则

如果嵌套的 CSS 规则无效,则所有包含的样式都将被忽略。这不会影响父规则或前面的规则。

在以下示例中,存在一个无效的选择器(% 不是选择器的有效字符)。包含此选择器的规则将被忽略,但后续的有效规则不会。

css
.parent {
  /* .parent styles these work fine */
  & %invalid {
    /* %invalid styles all of which are ignored */
  }
  & .valid {
    /* .parent .valid styles these work fine */
  }
}

另见