利用 SASS 簡化 `nth-child` 樣式的生成


考察如下的 HTML 片段,通過 CSS 的 nth-child() 偽選擇器實現列表的顏色循環,比如每三個一次循環。

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>

 

很容易地,我們能得出如下樣式代碼:

ul {
  li:nth-child(3n + 1) {
    color: indigo;
  }
  li:nth-child(3n + 2) {
    color: red;
  }
  li:nth-child(3n + 3) {
    color: green;
  }
}

 

以上,只三種顏色循環,簡單情況下可這樣處理。但樣式多起來之后,上面代碼顯得很臃腫且不易維護。

既然是使用 SASS,很容易想到可通過編寫循環語句來將其簡化。

循環過程就是遍歷一個預設的顏色列表,為每一個顏色生成相應的樣式。

List & Map

首先需要定義一個對象來保存預設的顏色列表,SASS 中的 Lists 可滿足條件。

$colors: indigo, red, green;

 

使用上面的 list:

$colors: indigo, red, green;

@each $color in $colors {
  .color-#{$color} {
    color: $color;
  }
}

 

編譯后輸出:

.color-indigo {
  color: indigo;
}

.color-red {
  color: red;
}

.color-green {
  color: green;
}

 

當然,也可以定義 Map 類型,為每種顏色指定名字,這樣使用的時候比較語義:

$colors: (
  "indigo": indigo,
  "red": red,
  "green": green
);

@each $name, $color in $colors {
  .color-#{$name} {
    color: $color;
  }
}

 

索引

列表對象及其遍歷如上,現在還需要在遍歷過程中獲得一個 index 索引值,用於生成 3*n+index

通過 index() 函數可以達到目的:

$colors: (indigo, red, green);

@each $color in $colors {
  $index: index($colors, $color);
  .color-#{$index} {
    color: $color;
  }
}

 

編譯后輸出:

.color-1 {
  color: indigo;
}

.color-2 {
  color: red;
}

.color-3 {
  color: green;
}

 

生成 nth-child 樣式

結合上面所有,可以得出生成 nth-child 樣式的代碼:

$colors: (indigo, red, green);

ul {
  @each $color in $colors {
    $index: index($colors, $color);
    li:nth-child(3n + #{$index}) {
      color: $color;
    }
  }
}

 

編譯后輸出:

ul li:nth-child(3n + 1) {
  color: indigo;
}
ul li:nth-child(3n + 2) {
  color: red;
}
ul li:nth-child(3n + 3) {
  color: green;
}

 

注意到 nth-child(3n + #{$index}) 中的 3 是硬編碼。既然我們的顏色是在一個數組中,所以我們是知道總個數的,也就能替換掉這里硬編碼的寫法。

通過 length() 函數可獲得 list 的長度。改進后的代碼如下:

$colors: (indigo, red, green);

ul {
  @each $color in $colors {
    $index: index($colors, $color);
    li:nth-child(#{length($colors)}n + #{$index}) {
      color: $color;
    }
  }
}

 

這樣,如果后續有顏色增加,或順序的調整,我們只需要更新 $colors 變量的值即可。

@mixin

進一步,我們可以將上面遍歷生成 nth-child 的代碼抽取成 mixin,這樣可以被其他地方使用。

$colors: (indigo, red, green);

@mixin nth-color {
  @each $color in $colors {
    $index: index($colors, $color);
    li:nth-child(#{length($colors)}n + #{$index}) {
      color: $color;
    }
  }
}

ul {
  @include nth-color;
}

 

mixin 的優化

但像上面這樣還不能達到完全公用,因為 mixin 中使用了 li 選擇器,不是所有需要 nth-child 樣式的地方,都使用的 li 元素,所以此處需要進行優化,使得 mixin 中的這部分內容靈活一點,以適用不同的使用環境。

首先,使用 & 父選擇器便可快速解決,這樣生成的樣式便由包含這個 mixin 的選擇器決定了。

$colors: (indigo, red, green);

@mixin nth-color {
  @each $color in $colors {
    $index: index($colors, $color);
    &:nth-child(#{length($colors)}n + #{$index}) {
      color: $color;
    }
  }
}

ul {
  li {
    @include nth-color;
  }
}

 

誠然,這樣修改過后,確實可以將該 mixin 使用於多個地方了。

但考慮這么一種情況,因為上面列表比較簡單,更加常見的情況是,列表中是另外復雜的元素,而不是單純的一個元素,比如像下面這樣:

<ul>
  <li>
    <div className="item">
      <h3>title</h3>
      <div>desc goes here...</div>
    </div>
  </li>
  <li>
    <div className="item">
      <h3>title</h3>
      <div>desc goes here...</div>
    </div>
  </li>
  <li>
    <div className="item">
      <h3>title</h3>
      <div>desc goes here...</div>
    </div>
  </li>
</ul>

 

現在想針對列表元素中的 h3 進行顏色的設置,即 nth-child 中設置的 color 只針對 h3 而不是整個 li 元素。

結合前面的優化,似乎可以這樣寫:

$colors: (indigo, red, green);

@mixin nth-color {
  @each $color in $colors {
    $index: index($colors, $color);
-    &:nth-child(#{length($colors)}n + #{$index}) {
+    &:nth-child(#{length($colors)}n + #{$index}) h3{
      color: $color;
    }
  }
}

ul {
  li {
    @include nth-color;
  }
}

 

編譯后的結果:

ul li:nth-child(3n+1) h3 {
  color: indigo;
}
ul li:nth-child(3n+2) h3 {
  color: red;
}
ul li:nth-child(3n+3) h3 {
  color: green;
}

 

從結果來看,達到了目標。但又在 nth-color 這個 mixin 中引入了 h3,使其變得不再通用,問題又回到了之前。

所以 & 只解決了 nth-child 父級嵌套的問題,對於 nth-child 后面還需要自定義選擇器的情況,就需要用別的方式進一步優化。

給 @mixin 傳參

mixin 是可以接收參數的,通過外部傳遞選擇器進來,可以達到將 h3 從 mixin 從剝離的目的。

@mixin nth-color($child) {
  @each $color in $colors {
    $index: index($colors, $color);
    &:nth-child(#{length($colors)}n + #{$index}) #{$child}{
      color: $color;
    }
  }
}

ul {
  li {
    @include nth-color("h3");
  }
}

 

從 @mixin 將參數傳遞到外面

最后,nth-color 這個 mixin 還有一處不夠靈活的地方,便是其樣式內容。

現在在其中寫死了只有一個 color: $color; 屬性,也就是說,使用該 mixin 的地方只能用它來設置元素的字色。如果 mixin 能夠將顏色傳遞出來,這樣外部使用的時候就可以用到其他屬性上,更加的靈活了。

SASS 確實也提供了這么種機制,即 mixin 能夠身外傳遞參數,借助 @content 指令。

@content 指令指代調用 mixin 時書寫的模式內容部分。如下的代碼直觀展示了其功能:

@mixin nth-color($child) {
  @each $color in $colors {
    $index: index($colors, $color);
    &:nth-child(#{length($colors)}n + #{$index}) #{$child}{
      color: $color;
+      @content;
    }
  }
}

ul {
  li {
-    @include nth-color("h3")
+    @include nth-color("h3"){
+      border:1px solid orange;
+    };
  }
}

 

編譯后內容為:

ul li:nth-child(3n+1) h3 {
  color: indigo;
  border: 1px solid orange;
}
ul li:nth-child(3n+2) h3 {
  color: red;
  border: 1px solid orange;
}
ul li:nth-child(3n+3) h3 {
  color: green;
  border: 1px solid orange;
}

 

可以看到,外部書寫的 border: 1px solid orange; 通過 @content 指代而放進了每個 nth-child 樣式中。只是這個 border 的顏色現在還是寫死的 orange,現在來將其設置成跟隨相應的字色,即跟着 color 屬性走。

當我們使用 @content(<arguments...>) 形式的 @content 時,可以傳遞一些參數,外部調用該 mixin 時需要使用如下形式來獲取傳遞的參數:

@include <name> using (<arguments...>) 

NOTE::這里的 using 語法只部分 SASS 實現(Dart Sass since 1.15.0, LibSass, Ruby Sass)中有支持,node-sass 還沒有。

最終的 mixin

所以,最終版的代碼為:

$colors: (indigo, red, green);

@mixin nth-color($child) {
  @each $color in $colors {
    $index: index($colors, $color);
    &:nth-child(#{length($colors)}n + #{$index}) #{$child} {
      color: $color;
      @content ($color);
    }
  }
}

ul {
  li {
    @include nth-color("h3") using($color) {
      border: 1px solid #{$color};
    }
  }
}

 

編譯后得到的 CSS:

ul li:nth-child(3n+1) h3 {
  color: indigo;
  border: 1px solid indigo;
}
ul li:nth-child(3n+2) h3 {
  color: red;
  border: 1px solid red;
}
ul li:nth-child(3n+3) h3 {
  color: green;
  border: 1px solid green;
}

 

總結

通過將生成 nth-child 樣式的規則自動化的過程中,順帶使用了 SASS 中另外一些特性,

  • 定義和使用數組及對象
  • 數組及對象的遍歷
  • 遍歷過程中索引的獲取
  • @mixin 的使用,參數的傳遞
  • @mixin 中 @content 的使用
  • @mixin 中向外傳遞參數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM