如何使用CSS偽類選擇器

2022-08-30 15:02:58

總覽

CSS選擇器允許你通過型別、屬性、位於HTML檔案中的位置來選擇元素。本教學闡述了三個新選項:is():where():has()

選擇器通常在樣式表中使用。下面的範例會找到所有<p>段落元素並將字重更改為粗體:

p {
  font-weight: bold;
}

你也可以在JavaScript中使用選擇器來找到DOM節點:

偽類選擇器根據HTML元素的當前狀態來定位它們。也許最廣為人知的是 :hover,它在遊標移到一個元素上時應用樣式,所以它被用來高亮可點選的連結和按鈕。

其他受歡迎的選項包括:

  • :visited:匹配存取過的連結
  • :target:匹配檔案URL所指向的元素
  • :first-child:指向第一個子元素
  • :nth-child:選擇指定子元素
  • :empty:匹配沒有內容或子元素的元素
  • :checked:匹配已勾選的核取方塊或無線電鈕
  • :blank:選擇使用者輸入為空的輸入框
  • :enabled:匹配一個被啟用的輸入框。如果一個元素能夠被啟用(如選擇、點選或接受文字輸入),或者能夠獲取焦點,則該元素是啟用的
  • :disabled:匹配一個被禁用的輸入框。在被禁用時,元素不能被啟用或獲取焦點
  • :required:指向必填的輸入框。所謂必填,就是在提交所屬表單之前,使用者必須為輸入框指定一個值
  • :valid:匹配一個內容驗證正確的輸入框
  • :invalid:匹配一個內容未通過驗證的輸入框
  • :playing:指向一個正在播放的audiovideo元素

瀏覽器最近又收到了三個偽類選擇器…

:is偽類選擇器

注意:這最初被指定為:matches():any(),但:is()已經成為CSS標準。

MDN解釋::is()CSS偽類函數將選擇器列表作為引數,並選擇該列表中任意一個選擇器可以選擇的元素。這對於以更緊湊的形式編寫大型選擇器非常有用。

你經常需要在不止一個元素上面應用相同的樣式。比如說,<p>段落文字顏色預設為黑色,但是當它位於<article><section><aside> 裡面時,文字顏色為灰色:

/* default black */
p {
  color: #000;
}

/* gray in <article>, <section>, or <aside> */
article p,
section p,
aside p {
  color: #444;
}

這是一個簡單的例子,但更復雜的頁面將導致更復雜和更冗長的選擇器字串。任何選擇器的語法錯誤都會破壞所有元素的樣式。

像Sass這樣的CSS前處理器允許巢狀(這也將出現在原生CSS中)。

article, section, aside {

  p {
    color: #444;
  }

}

這可以建立相同的CSS程式碼,減少打字的工作量,並可以防止錯誤。但是:

  • 在原生巢狀到來之前,你仍需要一個CSS構建工具。你可能想使用像Sass這樣的方案,但這可能給一些開發團隊引入複雜性。
  • 巢狀可能會導致其他問題。構建深度巢狀的選擇器是很容易的,但它會變得越來越難以閱讀以及輸出冗長的CSS。

:is() 提供了一個原生CSS解決方案。該特性已被所有現代瀏覽器支援(IE除外)。

:is(article, section, aside) p {
  color: #444;
}

單個選擇器可以包含任意數量的:is()偽類。比如說,下面的複雜選擇器將綠色文字顏色應用於所有<h1><h2><p>元素,這些元素是<section>的子元素,其包含類.primary.secondary,並且不是<article>的第一個子元素。

article section:not(:first-child):is(.primary, .secondary) :is(h1, h2, p) {
  color: green;
}

沒有 :is() 的同等程式碼需要六個CSS選擇器。

article section.primary:not(:first-child) h1,
article section.primary:not(:first-child) h2,
article section.primary:not(:first-child) p,
article section.secondary:not(:first-child) h1,
article section.secondary:not(:first-child) h2,
article section.secondary:not(:first-child) p {
  color: green;
}

注意,:is()無法匹配::before::after偽元素,因此下面範例程式碼會不起作用:

/* NOT VALID - selector will not work */
div:is(::before, ::after) {
  display: block;
  content: '';
  width: 1em;
  height: 1em;
  color: blue;
}

:where偽類選擇器

:where()選擇器語法與:is()相同,也被所有現代瀏覽器支援(IE除外)。這往往會導致相同的樣式。比如:

:where(article, section, aside) p {
  color: #444;
}

不同點在於優先順序。優先順序是用來決定哪個CSS選擇器應該覆蓋所有其他選擇器的演演算法。在下面的例子中,article p比單獨的p更加具體,因此所有位於<article>內的p元素的字型顏色將會是灰色:

article p { color: #444; }
p { color: #000; }

:is()的情況下,優先順序是在其引數中找到的最具體的選擇器。在:where()的情況下,優先順序為零。

考慮下面的CSS:

article p {
  color: black;
}

:is(article, section, aside) p {
  color: red;
}

:where(article, section, aside) p {
  color: blue;
}

讓我們將這個CSS應用到下面的HTML中:

<article>
  <p>paragraph text</p>
</article>

段落文字將被渲染為紅色,點選連結檢視CodePen範例

:is()選擇器與article p具有相同的優先順序,但它在樣式表的後面,所以文字變成了紅色。如有必要可以同時刪除article p:is()選擇器來應用藍色,因為:where()選擇器的優先順序比兩者都低。

更多的程式碼庫會使用:is()而不是:where()。然而,:where()的零優先順序對CSS重置來說是很實用的,它在沒有特定樣式的情況下應用標準樣式的基線。通常情況下,重置會應用一個預設的字型、顏色、內邊距和外邊距。

這個CSS重置程式碼對<h2>標題應用了1em的上外邊距,除非它們是<article>元素的首個子元素。

/* CSS reset */
h2 {
  margin-block-start: 1em;
}

article :first-child {
  margin-block-start: 0;
}

試圖在樣式表的後面設定一個自定義的<h2>上外邊距是沒有效果的,因為article :first-child有更高的優先順序:

/* never applied - CSS reset has higher specificity */
h2 {
  margin-block-start: 2em;
}

你可以用一個更高優先順序的選擇器來解決這個問題,但這需要更多的程式碼,而且對其他開發者來說不併不明顯。你最終會忘記你為什麼需要它。

/* styles now applied */
article h2:first-child {
  margin-block-start: 2em;
}

你也可以通過對每個樣式應用!important來解決這個問題,但請避免這樣做!它使進一步的樣式定義和開發變得更具挑戰性。

/* works but avoid this option! */
h2 {
  margin-block-start: 2em !important;
}

一個更好的選擇是在你的CSS重置中採用:where()的零優先順序。

/* reset */
:where(h2) {
  margin-block-start: 1em;
}

:where(article :first-child) {
  margin-block-start: 0;
}

現在,你可以覆蓋任何CSS重置樣式,無論其優先順序如何;不需要進一步的選擇器或!important

/* now works! */
h2 {
  margin-block-start: 2em;
}

:has()偽類選擇器

:has()選擇器使用了類似於:is():where()的語法,但它的目標是一個包含其他元素的元素。比如說,這裡是為任何包含一個或多個<img><section>標籤的<a>連結新增藍色、兩畫素寬的邊框的CSS:

/* style the <a> element */
a:has(img, section) {
  border: 2px solid blue;
}

這是幾十年來最激動人心的CSS進展!開發者們終於有了一種針對父元素的方法。

難以捉摸的"父選擇器"一直是人們請求最多的CSS特性之一,但它給瀏覽器供應商帶來了效能上的麻煩。因此,它已經即將到來了很長時間。簡而言之:

  • 瀏覽器在頁面上繪製元素時將CSS樣式應用於該元素。因此,在進一步新增子元素時,整個父元素必須重新繪製。
  • 在JavaScript中新增、刪除或修改元素可能會影響整個頁面的樣式,直到閉合的<body> 標籤為止。

假設供應商已經解決了效能問題,:has()的引入允許過去沒有JavaScript就不可能實現的成為可能。例如,當任何必填的內部欄位沒有校驗通過時,你可以設定外部表單<fieldset>和下面的提交按鈕的樣式。

/* red border when any required inner field is invalid */
fieldset:has(:required:invalid) {
  border: 3px solid red;
}

/* change submit button style when invalid */
fieldset:has(:required:invalid) + button[type='submit'] {
  opacity: 0.2;
  cursor: not-allowed;
}

這個例子新增了一個導航連結子選單指示器,其中包含一個子選單項的列表:

/* display sub-menu indicator */
nav li:has(ol, ul) a::after {
  display: inlne-block;
  content: ">";
}

或者,你可以新增偵錯樣式,比如高亮顯示所有沒有內部img<figure>元素。

/* where's my image?! */
figure:not(:has(img)) {
  border: 3px solid red;
}

在開啟你的編輯器和重構你的CSS程式碼庫之前,請注意:has()是很新的,支援比:is():where()更有限。它在Safari 15.4+Chrome 105+可用,但是到2023年應該可以廣泛使用。

總結

:is():where() 偽類選擇器簡化了 CSS 語法。你對巢狀和CSS前處理器的需求會減少。

:has()更具革命性和令人激動。父級選擇將迅速流行起來,我們將忘記黑暗時代。

以上就是本文的所有內容,如果對你有所幫助,歡迎點贊收藏轉發~