HTML/CSS

2013.08.27

使用 CSS nth-child 必須要注意的事情


之前有寫過一篇 利用 :nth-child() 讓網頁展示商品時左右對齊 的文章,裏面有稍微提到 :nth-child() 的使用方法。

其實 :nth-child() 好用的地方就在於他可以針對每個元素做個別的樣式設定,不用再像以往一樣用程式判斷然後算半天。不過我最近發現,有一些排版,沒辦法直接使用 :nth-child() 達到我想要的效果,所以最後我還是改用 jQuery 撰寫。如果你常使用 :nth-child() 寫網頁,不妨可以參考一下唷。

再次引用 W3C 的解釋(請特別記住這句話,接下來的範例,受到這句話的影響非常深刻唷)

:nth-child(n) 選擇器匹配屬於其父元素的第 N 個子元素,不論元素的類型。n 可以是數字、關鍵詞或公式。

【範例】規定屬於其父元素的第二個子元素的每個 p 的背景色:

p:nth-child(2) { background:#ff0000; }

CSS3 :nth-child() 選擇器 / W3SCHOOL

看完如果似懂非懂的朋友,沒關係,請先記住這句話,下面會再詳細解說。這篇文章先當作大家已經會使用基本的 :nth-child()

範例


以上這個網頁結構,分作主課程 (WordPress, CSS, Html) 以及子課程 (install plugin, install theme…等) 兩種;在範例中,我想要做的效果是「當子課程是偶數欄時,背景會變咖啡色」

左邊是用 CSS 的 :nth-child(odd) 語法,右邊則是用 jQuery 的 :odd selector ,大家可以看到最後呈現的兩種效果不太一樣。(是的,你沒看錯,odd 是奇數不是偶數,為什麼我會用 odd 而不用 even,後面會解釋)

就常理而言,應該是右邊的「Use jQuery(:odd)」會比較好閱讀,左邊的欄位變色感覺有些不連貫。如果你有看 codepen 的語法,應該會發現我都是使用:odd來控制,那為什麼計算的結果不一樣呢?難道是 CSS 跟 jQuery 對 odd 的計算方式不同嗎?

其實可以說是,也不是。事實上,這跟 HTML DOM 有比較大的關聯。

版面解析

讓我們先來拆解這個 HTML 版面。這是一個課程清單表,主課程底下會有很多子課程,所以用類似「合併儲存格」的功能,把最前面的主課程欄位合併,這樣在看課程的時候較一目了然。

因此,我是這麼寫的:

<div class="class-wrap">
  <div class="class">Wordpress<br /><span class="week">(Wednesday)</span></div>
  <div class="content">
    <div class="icon">
      <div class="icon-border">W</div>
    </div>
    <div class="course">install plugin</div>
    <div class="time">19:00 ~ 21:00</div>
    <div class="index"></div>
  </div>
  <div class="content">
    <div class="icon">
      <div class="icon-border">W</div>
    </div>
    <div class="course">install theme</div>
    <div class="time">14:00 ~ 18:00</div>
    <div class="index"></div>
  </div>
  <div class="content">
    <div class="icon">
      <div class="icon-border">W</div>
    </div>
    <div class="course">post your first atricle</div>
    <div class="time">18:00 ~ 20:00</div>
    <div class="index"></div>
  </div>
</div>

我用 .class-wrap 包住最大的課程類別,再用 .content 包住子課程。

所以簡化版的 HTML 會長這樣:

<div class="class-wrap">
  <div class="class">主課程標題 1</div>
  <div class="content">子課程 1</div>
  <div class="content">子課程 2</div>
  <div class="content">子課程 3</div>
</div>

<div class="class-wrap">
  <div class="class">主課程標題 1</div>
  <div class="content">子課程 1</div>
  <div class="content">子課程 2</div>
</div>

接著,回到原本我們想做的網頁效果:「子課程的偶數欄位要變色」。

我們想要變色的 class 是 .content,然後是偶數(even) 欄位。假使我們用 :nth-child 來寫的話,正常毫無疑問會這麼寫:

.content:nth-child(even) {
  background: #A69689;
}

但你會發現,用 :nth-child(even) 後,居然是奇數欄位變色了!! (也可以直接編輯我的 codepen 看結果唷)

揪竟是為什麼呢!?只能說 :nth-child 是個非常奇妙的東西,尤其碰到像範例這種複雜的排版,整個計算方式會跟常理認知的不太一樣呢!讓我們來慢慢的、一步步了解吧。


一、先了解:nth-child(n)的初始值是 0 還是 1 ?

:nth-child(n) 的初始值是 1 ,依序 1、2、3、4 這樣算下來。因此沒有 :nth-child(0) 這種東西

二、再了解:nth-child(n)的定義

重點 ★ 請把剛才我請你記住的那句話,關於 :nth-child(n) 的定義,拿出來複習一下:「:nth-child(n) 選擇器匹配屬於其父元素的第 N 個子元素,不論元素的類型」。

接著,我們再把剛剛的 CSS 語法: .content:nth-child(even),改成跟定義一樣的句子:「.content 選擇器,匹配其父元素 .class-wrap 的第偶數(even)個子元素」

… 好吧,我覺得大家一定看不懂,讓我改用白話文說一遍 (嗚嗚,要講解這一部分好難啊,不要逼我自暴自棄 T^T)

  1. 先找到 .content 的父元素 .class-wrap
  2. 找出 .class-wrap 底下的所有子元素,以剛剛的語法範例來看,第 1 個子元素是 class="class" 的主課程標題,第 2 個子元素才是 class="content" 的子課程 1 。 主課程標題 1 / 子課程 1 / 子課程 2
  3. 因此,原本我們以為「子課程 1 」會是匹配父元素的第 1 個元素,但因為 .content 前面還有一個 .class,所以「子課程 1 」變成了匹配父元素的第 2 個元素。
  4. 再重新把 .content:nth-child(even) 拿回來看,其實符合偶數(even)的元素是這些:

這樣大家清楚嗎?因為前面多了一個不是 .content 的 class,但他也是 .class-wrap 的子元素阿!所以全部算下來,原本我們以為的偶數會是奇數、奇數會變偶數。因此我的範例語法才會寫 :odd 而非 :even

如果了解,就讓我們繼續進行下去。

三、為什麼 n 的計算不是從 1 到 100 呢?

因為 :nth-child(n) 是以父元素 .class-wrap 作為起始點。假設第一個主課程底下有 4 個子課程,他的元素依序計算為 1、2、3、4;但,到了第二個主課程又會重新從 1 開始計算,並不會從 5 繼續數下去。

這就是為什麼我們看到左邊的範例會感覺參差不齊,但如果你把每個 .class-wrap 都當作一個段落,就會發現他其實偶數的元素還是會變色的。


圖解

那,到底每個子課程的 :nth-child(n) 是多少?

還是不太清楚的朋友,可以再看看我寫的 codepen:http://codepen.io/mukiwu/full/nmlkt ,然後隨意點選任何子課程的欄位。

如果你點選了任意子課程的欄位,可以在紅底的區塊看到一些數字。

簡單總結:

  • 「Use CSS nth-child(odd)」:以主課程作為區塊,每個主課程結束以後,會重新開始計算。這邊出現的數字是該欄位的 :nth-child(n) 的 n 值
  • 「Use jQuery(:odd)」:單純計算有多少子課程,不受主課程影響,所以會從 0 算到最大個數。這邊出現的數字是所有子課程的 index 值

以上是兩種寫法主要的差別。

利用 jQuery :odd 撰寫

就如前面所說,為了視覺的平衡,我們幾乎都會採用右邊的表現手法,所以可以改用 jQuery 撰寫:

var contentOdd = $("#jquery-trick .content:odd");
contentOdd.css("background" , "#A69689");
contentOdd.children(".course").css("color","#eee");

只是要特別注意的是,jQuery(":odd") 的初始值是 0 不是 1,所以第 1 個子課程是偶數、第 2 個子課程才是奇數… 以此類推。

我們以為偶數的欄位是奇數,以為奇數的欄位是偶數,這樣的情況會再度發生。

總結

如果你會用到像我這樣的排版,直覺的使用 CSS nth-child 來撰寫的話,也許會碰到同樣的問題。因此,可以嘗試使用 jQuery 解決。如果有可以直接用 CSS 解掉的方法,也歡迎告訴我唷:)

以後,我會將一些寫的較漂亮的範例轉移到 codepen,如果你喜歡我寫的範例,也歡迎到我的 codepen 支持我唷

歡迎給我點鼓勵,讓我知道你來過 :)

guest
3 則留言
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Carl Shih
Carl Shih
6 years ago

謝謝了~!! 剛開始學這個真的容易沒注意到是”父”元素的第幾個!

LCM
LCM
3 years ago

用CSS做出類似jQuery的效果
https://codepen.io/jimmyliao11/pen/gBYMxN

Copyright (C) MUKI space* / Reborn Theme All Rights Reserved.