使用 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 */
  }
}

另見