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