大家好,我是 Eric。
昨天文章的最後,丟出了幾個問題,沒有辦法透過基本的選擇器來完成,又或者說,要透過基本選擇器來完成會變得非常麻煩。以「我的表格想要偶數列跟奇數列有不同的顏色」為例,我們可以嘗試用類別的方式來指定:
<table>
<tr class="odd">
<td>這是第一列第一欄</td>
<td>這是第一列第二欄</td>
</tr>
<tr class="even">
<td>這是第二列第一欄</td>
<td>這是第二列第二欄</td>
</tr>
</table>
tr.odd {
background: #fff;
}
tr.even {
background: #a0a0a0;
}
但如果是更複雜的樣式,就會需要透過其他的選擇器來完成。今天,我們就要來看看這些更進階的選擇器:虛擬元素 (pseudo element)、虛擬類別 (pseudo class) 以及同層級結合器 (sibling combinator)。
虛擬元素的基本語法如下:
選擇器::虛擬元素 {
content: "{虛擬元素內容}";
樣式名稱: 樣式值;
}
虛擬元素,顧名思義,是「不存在於 HTML 文件中的元素」。意思是:當我們下載一份 HTML 檔案時,我們不會看到這些元素。這些元素,是透過 CSS 讓他們存在。不同於下一小節介紹的虛擬類別,有許多不同種類。虛擬元素只有兩種:::before
與 ::after
。
正因為他們是虛擬的,所以虛擬元素中必然會有一個 content
的屬性。
比起創造內容,虛擬元素更常用來製作一些特殊的樣式,譬如對話框。有一個名為《Bubbly》的網站,可以自動生成對話框的 CSS 樣式。
我們可以從 style.css 看到第 104 行來看看 Twenty Twenty 如何使用虛擬元素。
/* style.css Line 104 */
blockquote::before,
blockquote::after {
content: "";
}
這段語法在清除 <blockquote>
這個元素前後可能會出現的引用標記,避免有些瀏覽器會自動轉譯出不符合預期的樣式。
而第 427 行則是透過虛擬元素的方法,讓水平線製作出 // 這樣的水平線:
/* style.css Line 427 */
hr.styled-separator::before,
hr.styled-separator::after {
background: currentColor;
content: "";
display: block;
height: 1.6rem;
position: absolute;
top: calc(50% - 0.8rem);
transform: rotate(22.5deg);
width: 0.1rem;
}
.entry-content hr::before,
hr.styled-separator::before {
left: calc(50% - 0.5rem);
}
.entry-content hr::after,
hr.styled-separator::after {
right: calc(50% - 0.5rem);
}
如同虛擬元素,虛擬類別作為選擇器,需要配合其他選擇器,不能單獨存在。常用來表示選擇元素的狀態 (如 :hover
) 及角色 (如 nth-child
)。基本語法如下:
/* 注意虛擬元素有兩個冒號,而虛擬類別只有一個 */
選擇器:虛擬類別 {
樣式名稱: 樣式值;
}
常見的虛擬類別有 :hover
、:focus
、:first-child
、:last-child
、:nth-child
與 :not
。
這兩種虛擬類別代表著游標暫留 (hover) 於元素上,以及聚焦 (focus) 於元素上 (例如:點擊表單 <input>
欄位後,欄位中的游標會閃爍) 的狀態。
常見的搭配組合如下:
a:hover {
opacity: 0.8;
/* 游標暫留於連結時,不透明度會降為 0.8 */
}
input[type="text"]:focus {
background-color: #000;
color: #fff;
/* 游標聚焦於單行文字表單時,變成黑底白字 */
}
在 style.css 的第 456 行,我們可以看到:
a:hover,
a:focus {
text-decoration: none;
}
這裡定義了所有的連結在滑鼠暫留,或聚焦於連結上的時候 (譬如持續按鍵盤的 tab 鍵,直到選到連結時),連結文字預設的底線就會消失。
我們再往下看到第 817 行:
button:focus,
button:hover,
.button:focus,
.button:hover,
.faux-button:focus,
.faux-button:hover,
.wp-block-button .wp-block-button__link:focus,
.wp-block-button .wp-block-button__link:hover,
.wp-block-file .wp-block-file__button:focus,
.wp-block-file .wp-block-file__button:hover,
input[type="button"]:focus,
input[type="button"]:hover,
input[type="reset"]:focus,
input[type="reset"]:hover,
input[type="submit"]:focus,
input[type="submit"]:hover {
text-decoration: underline;
}
與 a.focus
的時候相反,這裡則是當滑鼠暫停於按鈕元素時,顯示文字底線。
基本語法:
選擇器:first-child,
選擇器:last-child,
選擇器:nth-child(n) {
樣式名稱: 樣式值;
}
Child 系列,是在告訴瀏覽器要「選擇第幾個 {{選擇器}}」。
以 style.css 第 3002 行為例:
/* style.css Line 3002 */
.wp-block-column > *:first-child {
margin-top: 0;
}
.wp-block-column > *:last-child {
margin-bottom: 0;
}
上述的 wp-block-column
類別,主要用於 WordPress 區塊編輯器中的 [內容欄] 區塊。個別欄位的 wp-block-column
底下第一個元素 (用 *
代表任何元素) 的上邊界會是 0,而最後一個元素的下邊界也會是 0。
舉例來說:
<div class="wp-block-columns">
<div class="wp-block-column">
<h2>第一個欄位的標題,上邊界是 0</h2>
<p>第一個欄位的內容</p>
<span>第一個欄位最底下的註腳,下邊界是 0</span>
</div>
<div class="wp-block-column">
<h2>第二個欄位的標題,上邊界是 0</h2>
<p>第二個欄位的內容</p>
<span>第二個欄位最底下的註腳,下邊界是 0</span>
</div>
</div>
而第 3307 行則顯示如何運用 nth-child
,這個例子剛好回答了我們第一段的問題:「我的表格想要偶數列跟奇數列有不同的顏色」。
/* style.css Line 3307 */
.wp-block-table.is-style-stripes tbody tr:nth-child(odd) {
background: #dcd7ca;
}
透過 tr:nth-child(odd)
這個選擇器,我們定義了各個奇數列的背景顏色。至於要指定偶數列的話,我們可以透過 tr:nth-child(even)
來選擇。
除了奇數偶數外,tr:nth-child(3n+1)
可以指定第 1、4、7 ... 3n+1 列。
使用 nth-child
時,要特別注意,在計算順序時,要考慮的是「在同階層中第幾個元素」。舉例來說
div.me:nth-child(3n+1) {
background-color: #000;
}
<div class="parent">
<div class="me">我是第 1 個 div.me,是第 1 個子層級元素,會被選到。</div>
<div class="me">我是第 2 個 div.me,是第 2 個子層級元素,不會被選到。</div>
<div class="me">我是第 3 個 div.me,是第 3 個子層級元素,不會被選到。</div>
<div>我不是 div.me,是第 4 個子層級元素,不會被選到。</div>
<div class="me">我是第 4 個 div.me,是第 5 個子層級元素,不會被選到。</div>
<div class="me">我是第 5 個 div.me,是第 6 個子層級元素,不會被選到。</div>
<div class="me">我是第 6 個 div.me,是第 7 個子層級元素,會被選到。</div>
</div>
基本語法:
選擇器a:not(選擇器b) {
樣式名稱: 樣式值;
}
昨天的範例中,我們問了另一個問題:「我想要指定『不是 xx 類型』的元素」。用的就是 :not
選擇器。
我們來看看 style.css 中的第 422 行,是怎麼運用 :not
。
/* style.css Line 422 */
.entry-content hr:not(.has-background),
hr.styled-separator {
color: #6d6d6d;
}
這個例子中,Twenty Twenty 選擇在類型 entry-content
底下「不具有 has-background
類型」的水平線 <hr>
,並將這些水平線的顏色變成灰色 (#6d6d6d
)。
除了昨天提到的選擇子層級外,我們有時候也會需要套用同層級的選擇器。譬如說「緊鄰頁首主視覺的第一個 <h1>
元素」,這時候,我們就會需要使用同層級結合器。
跟子層級結合器一樣,同層級結合器也可以區分為精確比對 (adjacent sibling combinator,更精確的說是「相鄰比對」),與廣泛比對 (general sibling combinator)。
基本語法:
選擇器 a + 選擇器 b {
樣式名稱: 樣式值;
}
我們可以在 style.css 的第 681 行看到相關案例:
/* style.css Line 681 */
label.inline,
input[type="checkbox"] + label {
display: inline;
font-weight: 400;
margin-left: 0.5rem;
}
這段語法中,我們可以看到,具有 inline
類型的 <label>
標籤,以及與核取方塊 <input type="checkbox">
相鄰的標籤 (通常指的是在核取方塊右邊的敘述標籤),它們的字重會是 400,而左邊界的寬度則是 0.5rem。
<form>
<input type="text" name="name" id="name"/>
<label class="inline" for="name">姓名 (會套用樣式)</label>
<input type="checkbox" name="check1" id="check1">
<label for="check1">我會套用樣式</label>
<input type="checkbox" name="check2" id="check2">
<label for="check2">我也會套用樣式</label>
<input type="checkbox" name="check3" id="check3">
<span>我來阻止你們結合</span>
<label for="check3">我不會套用樣式</label>
</form>
基本語法:
選擇器 a ~ 選擇器 b {
樣式名稱: 樣式值;
}
由於在 Twenty Twenty 的 style.css 中找不到實際案例,以下我們一樣將前面的選擇器稍加調整:
/* style.css Line 681 */
label.inline,
input[type="checkbox"] ~ label {
display: inline;
font-weight: 400;
margin-left: 0.5rem;
}
<form>
<input type="text" name="name" id="name"/>
<label class="inline" for="name">姓名 (會套用樣式)</label>
<input type="checkbox" name="check1" id="check1">
<label for="check1">我會套用樣式</label>
<input type="checkbox" name="check2" id="check2">
<label for="check2">我也會套用樣式</label>
<input type="checkbox" name="check3" id="check3">
<span>我來阻止你們結合</span>
<label for="check3">我照樣會套用樣式</label>
</form>
必須注意的是,同層級結合器只會選擇「選擇器 a 之後」的同層級選擇器。換句話說,下面的情況無法透過相鄰、或是廣泛同層級結合器來選擇:
<form>
<input type="text" name="name" id="name"/>
<label class="inline" for="name">姓名 (會套用樣式)</label>
<label for="check1">選不到我</label>
<input type="checkbox" name="check1" id="check1">
<input type="checkbox" name="check2" id="check2">
<label for="check2">我也會套用樣式</label>
<input type="checkbox" name="check3" id="check3">
<span>我來阻止你們結合</span>
<label for="check3">我照樣會套用樣式</label>
</form>
由於同層級結合器在講解上稍微抽象,所以可以參考我在 Codepen 上時做的示範。