CSS Podcast - 015:虛擬類別
假設您有一個電子郵件註冊表單,且希望電子郵件表單欄位在含有無效電子郵件地址時顯示紅色邊框。該怎麼做?您可以使用 :invalid
CSS 虛擬類別,這是瀏覽器提供的眾多虛擬類別之一。
偽類別可讓您根據狀態變更和外部因素套用樣式。 也就是說,設計可以回應使用者輸入的內容,例如無效的電子郵件地址。 這些內容會在選取器單元中說明,本單元則會詳細介紹。
與虛擬元素不同 (詳情請參閱上一個單元),虛擬類別會連結至元素可能處於的特定狀態,而不是一般樣式元素的部分。
互動狀態
使用者與網頁互動時,會套用下列虛擬類別。
:hover
如果使用者有滑鼠或觸控板等指標裝置, 並將指標放在元素上, 您可以使用 :hover
勾住該狀態,套用樣式。 這項屬性可提示元素可供互動。
:active
當使用者與元素互動 (例如點選) 時,系統會觸發這個狀態,直到放開點選為止。如果使用滑鼠等指標裝置,這個狀態是指點選開始但尚未放開。
:focus
、:focus-within
和 :focus-visible
如果元素可以接收焦點 (例如 <button>
),您可以使用 :focus
虛擬類別對該狀態做出反應。
如果元素的子項元素收到焦點,您也可以使用 :focus-within
做出反應。
可聚焦的元素 (例如按鈕) 聚焦時會顯示焦點環,即使點選也是如此。在這種情況下,開發人員會套用下列 CSS:
button:focus { outline: none; }
當元素收到焦點時,這項 CSS 會移除預設的瀏覽器焦點環,這會對使用鍵盤瀏覽網頁的使用者造成無障礙問題。如果沒有焦點樣式,使用者就無法在使用 Tab 鍵時追蹤目前的焦點位置。使用 :focus-visible
,您可以在元素透過鍵盤取得焦點時呈現焦點樣式,同時使用 outline: none
規則,在指標裝置與元素互動時防止呈現焦點樣式。
button:focus { outline: none; } button:focus-visible { outline: 1px solid black; }
:target
:target
虛擬類別會選取具有與網址片段相符的 id
元素。 假設您有下列 HTML:
<article id="content"> <!-- ... --> </article>
如果網址包含 #content
,您可以將樣式附加至該元素。
#content:target { background: yellow; }
這項功能有助於醒目顯示可能已特別連結的區域,例如使用跳轉連結的網站主要內容。
歷史狀態
:link
:link
虛擬類別可套用至任何具有href
值,但尚未造訪的 <a>
元素。
:visited
您可以使用 :visited
虛擬類別,為使用者已造訪的連結設定樣式。這與 :link
的狀態相反,但基於安全考量,您可使用的 CSS 屬性較少。您只能為 color
、background-color
、border-color
、outline-color
和 SVG fill
和 stroke
的顏色設定樣式。
順序很重要
如果您定義 :visited
樣式,至少具有相同特異性的連結虛擬類別可以覆寫該樣式。因此,建議您使用 LVHA 規則,依特定順序為含有虛擬類別的連結設定樣式::link
、:visited
、:hover
、:active
。
a:link {} a:visited {} a:hover {} a:active {}
表單狀態
下列虛擬類別可選取表單元素,以及這些元素在互動期間可能處於的各種狀態。
:disabled
和:enabled
如果瀏覽器停用表單元素 (例如 <button>
),您可以使用 :disabled
虛擬類別掛鉤至該狀態。:enabled
虛擬類別適用於相反狀態,但表單元素預設也是 :enabled
,因此您可能不會使用這個虛擬類別。
:checked
和:indeterminate
當支援的表單元素 (例如核取方塊或圓形按鈕) 處於勾選狀態時,即可使用 :checked
虛擬類別。
:checked
狀態是二進位 (true 或 false) 狀態,但核取方塊在勾選或取消勾選時,確實有中間狀態。這就是所謂的 :indeterminate
狀態。
舉例來說,如果您有「全選」控制項,可勾選群組中的所有核取方塊,就會出現這種狀態。如果使用者隨後清除其中一個核取方塊,根核取方塊就不再代表「全部」已勾選,因此應處於不確定狀態。
<progress>
元素也有不確定狀態,可設定樣式。 常見的用途是顯示條紋外觀,表示還需要多少資源尚不清楚。
:placeholder-shown
如果表單欄位有 placeholder
屬性,但沒有值,則可以使用 :placeholder-shown
虛擬類別,將樣式附加至該狀態。只要欄位中有內容,無論是否含有 placeholder
,這個狀態就不會再適用。
驗證狀態
您可以使用 :valid
、:invalid
和 :in-range
等虛擬類別,回應 HTML 表單驗證。 :valid
和 :invalid
虛擬類別適用於電子郵件欄位等情境,這類欄位必須有相符的 pattern
,才能成為有效欄位。這個有效值狀態會顯示給使用者,協助他們瞭解可以安全地移至下一個欄位。
如果輸入內容有 min
和 max
,例如數值輸入 和值位於這些界限內,則可以使用 :in-range
虛擬類別。
使用 HTML 表單時,您可以透過 required
屬性判斷欄位是否為必填。:required
虛擬類別適用於必填欄位。您可以使用 :optional
虛擬類別選取非必填欄位。
依索引、順序和出現次數選取元素
有一組虛擬類別會根據項目在文件中的位置選取項目。
:first-child
和:last-child
如要尋找第一個或最後一個項目,可以使用 :first-child
和 :last-child
。這些虛擬類別會傳回同層級元素群組中的第一個或最後一個元素。
:only-child
您也可以使用 :only-child
虛擬類別,選取沒有同層級的元素。
:first-of-type
和:last-of-type
您可以選取 :first-of-type
和 :last-of-type
,這兩個標記一開始看起來與 :first-child
和 :last-child
相同,但請考慮以下 HTML:
<div class="my-parent"> <p>A paragraph</p> <div>A div</div> <div>Another div</div> </div>
以及這個 CSS:
.my-parent div:first-child { color: red; }
由於第一個子項是段落,而非 div,因此不會有任何元素標示為紅色。:first-of-type
虛擬類別在這個情況下很有用。
.my-parent div:first-of-type { color: red; }
即使第一個 <div>
是第二個子項,但仍是 .my-parent
元素內的第一個型別,因此會套用這項規則,並顯示為紅色。
:nth-child
和:nth-of-type
您也可以使用其他子項和型別。 :nth-child
和 :nth-of-type
虛擬類別可讓您指定位於特定索引的元素。CSS 選取器中的索引從 1 開始。
:nth-last-child()
和 :nth-last-of-type()
虛擬類別會從結尾開始計算,而非開頭。
您也可以將索引以外的項目傳遞至這些虛擬類別。 如要選取所有偶數元素,可以使用 :nth-child(even)
。
您也可以使用 An+B 微語法,建立更複雜的選取器,以固定間隔尋找項目。
li:nth-child(3n+3) { background: yellow; }
這個選取器會選取每三個項目中的一個, 從第 3 個項目開始。這個運算式中的 n
是索引,從零開始,而 3 (3n
) 則是索引的乘數。
假設您有 7 個 <li>
項目。 選取的第一個項目是 3,因為 3n+3
會轉譯為 (3 * 0) + 3
。 下一次疊代會挑選項目 6,因為 n
現在已遞增為 1
, 因此 (3 * 1) + 3)
。 這個運算式適用於 :nth-child
和 :nth-of-type
。
:nth-child()
和 :nth-last-child()
也支援「of S」語法,可讓您使用選取器篩選比對項目,類似於 :nth-of-type()
。li:nth-of-type(even)
相當於 :nth-child(even of li)
。:nth-of-type
只允許您依據元素類型 (例如 li
或 p
) 篩選,但「of S」語法允許您依據任何選取器篩選。
如果表格很長,建議在每隔一列的位置加上條紋,雖然您可以使用 tr:nth-child(even)
指定每隔一個資料列,但如果篩除部分資料列,這個方法就無法運作。如果您透過套用 hidden
屬性來實作篩選功能,可以在選取器中加入 of :not([hidden])
,預先篩除隱藏項目,再選取偶數列。
tr:nth-child(even of :not([hidden])){ background: lightgrey; }
您可以在這個 nth-child 測試工具或這個數量選取器工具上,試用這類選取器。
:only-of-type
最後,您可以在同層級元素群組中,找出特定類型的唯一元素: :only-of-type
。 如果您想選取只有一個項目的清單,或是想找出段落中唯一加粗的元素,這項功能就非常實用。
尋找空白元素
有時識別完全空白的元素可能很有用,而這也有對應的虛擬類別。
:empty
如果元素沒有子項,則適用 :empty
虛擬類別。不過,子項不只是 HTML 元素或文字節點,也可能是空白字元,因此在偵錯下列 HTML 時,如果想知道為何無法搭配 :empty
使用,可能會感到困惑:
<div> </div>
原因是開頭和結尾 <div>
之間有空白字元,因此 :empty
無法運作。
如果您對 HTML 的控制權較少,且想要隱藏空白元素 (例如 WYSIWYG 內容編輯器),:empty
虛擬類別就非常實用。這裡的編輯者新增了空白段落。
<article class="post"> <p>Donec ullamcorper nulla non metus auctor fringilla.</p> <p></p> <p>Curabitur blandit tempus porttitor.</p> </article>
你可以使用 :empty
找出並隱藏該內容。
.post :empty { display: none; }
尋找及排除多個元素
部分虛擬類別可協助您編寫更精簡的 CSS。
:is()
如要找出 .post
元素中的所有 h2
、li
和 img
子項元素,您可能會想編寫如下的選取器清單:
.post h2, .post li, .post img { … }
使用 :is()
虛擬類別,可以編寫更精簡的版本:
.post :is(h2, li, img) { /* ... */ }
:is
虛擬類別不僅比選取器清單更精簡,也更寬容。 在大多數情況下,如果選取器清單中含有錯誤或不支援的選取器,整個選取器清單就會失效。如果 :is
虛擬類別中傳遞的選取器有誤,系統會忽略無效的選取器,但會使用有效的選取器。
:not()
您也可以使用 :not()
虛擬類別排除項目。舉例來說,您可以使用這項功能,為沒有 class
屬性的所有連結設定樣式。
a:not([class]) { color: blue; }
:not
偽類別也有助於提升無障礙功能。 舉例來說,<img>
必須有 alt
,即使是空值也一樣,因此您可以編寫 CSS 規則,為無效圖片新增粗紅色的外框:
img:not([alt]) { outline: 10px red; }
:has()
如果想根據元素內含的內容設定樣式,該怎麼做?您可以使用 :has()
虛擬類別來執行這項操作。舉例來說,您可能想為包含圖示的按鈕套用樣式。
button:has(svg) { /* ... */ }
在最基本的設定中 (如上一個範例所示),您可以將 :has()
視為父項選取器。您也可以搭配其他選取器使用相符的父項選取器,指定其他元素。
form:has(input:valid) label { font-weight: bold; } form:has(input:valid) label::after { content: "✅"; }
在這個範例中,當表單輸入含有 valid
虛擬類別時,我們會將樣式套用至標籤元素和 label::after
虛擬元素。
:has()
虛擬類別無法巢狀內嵌在另一個 :has()
中,但可以與其他虛擬類別合併使用。
:is(h1, h2, h3):has(a) { /* ... */ }
選取器清單不容有誤,因此如果清單中有任何選取器無效,系統就會忽略所有樣式規則。
.my-element:has(img, ::before) { /* any styles here will be discarded since pseudo elements can't be included in the :has() selector list */ }
隨堂測驗
測試您對虛擬類別的瞭解
虛擬類別的作用就像是動態套用至元素的類別,而虛擬元素則會作用於元素本身。
:
做為鍵的區別字元下列哪些是函式虛擬類別?
:is()
:target
()
,表示接受參數。:empty
()
,表示接受參數。:not()
下列哪些虛擬類別是由使用者互動所致?
:hover
:press
:squeeze
:target
:focus-within
下列哪些是 <form>
狀態虛擬類別?
:enabled
:fresh
:indeterminate
:checked
:in-range
:loading
:valid