1. 程式人生 > 其它 >CSS Selectors Level 4新特性全面解析

CSS Selectors Level 4新特性全面解析

前言

當看到 CSS Selectors Level 4 很多人會理所當然地喊出 CSS4。但是,這裡必須明確一個概念,目前所謂的 CSS3 和 CSS4 都是 CSS2.1 以後對某一些 CSS 模組進行升級更新後的稱呼。CSS3 和 CSS4 永遠都不會出現,它們只是為了區分 CSS 模組升級後的等級,例如有些 CSS 選擇器在之前就存在了,但是此時我們為它添加了新的特性,那麼這個模組就升級到了 CSS Selectors Level 3 中,如果再一次升級,那麼就變成了 CSS Selectors Level 4。下面我們可以對目前 W3C 工作草案中的 CSS Selectors Level 4 新特性進行一個階段性總結,之所以叫階段性,是因為未來可能有更多的新特性加入。

Negation pseudo-class——:not()

:not() 用於將符合規則的元素剔除,將樣式規則應用於其他元素上。在 CSS3 中已經有 :not(),不過在 CSS3 中只能使用簡單的匹配規則,例如 :not(p) 用來選擇不是 <p></p> 的元素。而在 CSS4 中,可以應用更復雜的匹配規則,但是同樣地不允許巢狀使用,例如 :not(:not(...))

.negation {  color: black;}.negation .default:not([data-red="no"]) {  color: red;}.negation .default a {  color: green;}.negation .default a:not([rel="green"], [rel="default"]) {  color: blue;}
<div class="negation">
  <div class="default" data-red="no">
    <a href="http://www.baidu.com" rel="green">這裡是綠色</a>
    <a href="http://www.ele.me" rel="default">這裡也是綠色</a>
    <a href="http://www.sina.com" rel="blue">這裡是藍色</a>
  </div>
  <div class="default" data-red="no">
    這裡是黑色  </div>
  <div class="default" data-red="yes">
    這裡是紅色  </div></div>

:not() 小提示

我們可以利用 :not() 來對 CSS 樣式進行一個優先順序提升,例如 div:not(span) {…}div {…} 是同個概念,但是明顯地前者的優先順序更高。

想解鎖更多 :not() 的使用姿勢就去看 The Negation Pseudo-class 草案

Matches-any Pseudo-class——:matches 偽類

:matches() 用於匹配所述規則的元素,並應用相應的樣式規則,同樣不允許巢狀使用,-webkit-any()-moz-any() 是它的兩個相容性寫法。它可以讓我們節省書寫大量的 CSS 樣式匹配規則,讓我們從大量重複的規則書寫中解放出來。

.matches {  color: black;}.matches :matches(span, div) :matches(span, div) {  color: green;}/*
等同於
.matches span div,
.matches span span,
.matches div span,
.matches div div {
  color: green;
}
 */.matches :-webkit-any(span, div) :-webkit-any(span, div) {  color: green;}.matches :-moz-any(span, div) :-moz-any(span, div) {  color: green;}.matches :matches(.a, .b) :matches(.a, .b) {  color: red;}/*
等同於
.matches .a .a,
.matches .a .b,
.matches .b .a,
.matches .b .b {
  color: red;
}
 */.matches :-webkit-any(.a, .b) :-webkit-any(.a, .b) {  color: red;}.matches :-moz-any(.a, .b) :-moz-any(.a, .b) {  color: red;}
<div class="matches">
  <span>
    <div>綠色</div>
  </span>
  <span>
    <span>綠色</span>
  </span>
  <div>
    <span>綠色</span>
  </div>
  <div>
    <div>綠色</div>
  </div>
  <div class="a">
    <div class="b">紅色</div>
  </div>
  <div class="b">
    <div class="a">紅色</div>
  </div>
    <div class="a">
    <div class="a">紅色</div>
  </div>
  <div class="b">
    <div class="b">紅色</div>
  </div></div>

Case-Sensitivity——不區分大小寫匹配標識

Case-Sensitivity 用於宣告某個匹配規則中,對字串或者某個 value 的匹配不區分大小寫。該標誌聲明於 ] 即右中括號之前,例如 [data-value="case" i],其中的 i 就是 Case-Sensitivity 標識。但是如果我們需要明確區分大小寫區別的時候,該標識可能會導致某些不可意料的後果,所以使不使用該標識應該明確使用的場景是否對資料來源的大小寫敏感。

.case-sensitivity :matches([data-value="case" i]) {  color: yellow;}
<div class="case-sensitivity">
  <p data-value='Case'>Case</p>
  <p data-value="case">case</p></div>

以上的例子,data-value 雖然既有大寫也有小寫,但是由於我們聲明瞭 Case-Sensitivity,所以無論大小寫都會被匹配。像例子中 caseCaseCASE 等都會被匹配。

The Directionality Pseudo-class——:dir()

:dir() 用於匹配符合某個方向性的元素,例如 :dir(ltr)dir(rtl)。顧名思義,ltr 表示 left to right,即方向從左到右,rtl 表示 right-to-left,即方向從右到左。值得注意的是,使用 :dir() 匹配元素和使用 [dir=...] 在某個程度上是一樣的效果,但是一個區別是 [dir=...] 無法匹配到沒有顯示宣告 dir 的元素,但是 :dir() 卻可以匹配到由瀏覽器計算得到或者繼承來的 dir 屬性的元素,詳情可以看一下草案。因此,如果我們有明確地對某個元素宣告 dir,那我們大可以使用 [dir=...] 的形式來匹配某個元素,但是如果我們只是單純從父元素繼承而來的 dir,那麼此時還是需要用到:dir()

.dir :dir(ltr) {  color: blue;}.dir :dir(rtl) {  color: green;}
<div class="dir">
  <p dir="ltr">從左到右</p>
  <p dir="rtl">從右到左</p></div>

The Language Pseudo-class——:lang()

:lang() 用於匹配聲明瞭 lang=value 的元素,並且可以使用萬用字元匹配,例如 p:lang(*-CH) 將可以匹配 de-CHp 元素。

.lang p:lang(de-DE) {  color: green;}.lang p:lang(*-CH) {  color: blue;}
<div class="lang">
  <p lang="de-DE-1996">de-DE-1996</p>
  <p lang="de-CH">de-CH</p></div>

:any-link 用於匹配帶有 href 屬性的超連結元素,例如 <a><area><link> 等帶有 href 屬性的元素。:-webkit-any-link:-moz-any-link 是它的相容性寫法。目前工作組對該選擇器的命名尚不滿意,未來該選擇器可能會修改其名字。該選擇器的作用在於可以選出所有帶有連結的元素,如果使用舊方法,那麼只能使用標籤名的方式或者a[href=value] 的方式去匹配。

.link a:any-link {  color: red;}.link a:-webkit-any-link {  color: red;}.link a:-moz-any-link {  color: red;}
<div class="link">
  <a href="#">我是帶有顏色的超連結</a></div>

The contextual reference element pseudo-class——:scope

:scope 用於匹配當前作用域下的頂級元素。但是目前 <style scoped> 已經被移除——issue,所以 :scope 基本等效於:root

<div class="scope">
  <p>This paragraph is outside the scope.</p>
  <div>
    <style scoped>
      :scope {        background-color: red;      }
      p {        color: blue;      }
    </style>
    <p>This paragraph is inside the scope.</p>
  </div></div>

以上程式碼,第二個 div 將會有紅色背景,並且他的所有 <p> 子元素都將擁有藍色文字。

Time-dimensional Pseudo-classes——:current(), :past(), :future()

我個人用 時間軸偽類 統一稱呼 :current(), :past(), :future() 這三個偽類。:current() 匹配時間軸當前的元素,:past() 匹配 :current()元素之前的元素,:future() 則匹配當前時間軸後的所有元素。這裡說的時間軸指的是例如WebVTT。值得注意的是,規範中寫道如果使用的時間軸並不是文件語言所規定的,那麼 :past():future() 有可能分別匹配 :current() 元素的前面的兄弟元素和後面的兄弟元素。由於在 Chrome Canary 和 Safari TP 上都不支援這幾個偽類,所以無法實驗正確性。下面使用的例子是從這個網址摘過來的。

:current(p, span) {  background-color: yellow;}:past(p, span),:future(p, span) {  background-color: gray;}
<video controls preload="metadata">
  <source src="http://html5demos.com/assets/dizzy.mp4" type="video/mp4" />
  <source src="http://html5demos.com/assets/dizzy.webm" type="video/webm" />
  <source src="http://html5demos.com/assets/dizzy.ogv" type="video/ogv" />

  <track label="English" kind="subtitles" srclang="en" src="http://www.iandevlin.com/html5test/webvtt/upc-video-subtitles-en.vtt" default></video>

The Indeterminate-value Pseudo-class——:indeterminate

radiocheckbox 元素上一般有兩種狀態——選中未選中,但是有的時候的狀態會是不確定狀態,而:indeterminate 就是匹配這種不確定狀態的 radiocheckbox

:indeterminate + label {  background-color: gray;}
<input type="radio" name="name" id="test"><label for="test">未確定狀態</label>

通常地,radiocheckbox 在沒有宣告選中狀態時,預設只有兩種可能性:checkedunchecked,為了讓他們出現第三種狀態,我們可以藉助 JS 來控制:

document.querySelector('#test').indeterminate = true;

上面例子的 <label><input> 處於 indeterminate state 的時候,文字將會變為灰色。

The default-option pseudo-class——:default

:default 匹配一組相似元素集合中的預設元素,例如 <form> 中有多個 <input>,其中有一個是 <input type="submit">,那麼該元素將會被匹配。此外還有 <option> 也有預設元素。

.default .default-form :default {  background-color: gray;}
<div class="default">
  <form class="default-form" action="#" method="get">
    <input type="submit" name="name" value="submit">
    <input type="reset" name="name" value="reset">
  </form></div>

The validity pseudo-classes——:valid, :invalid

<input type="email"> 中,如果我們輸入了 abc123,那麼此時 :invalid 將會匹配該元素,假如我們輸入[email protected],那麼此時 :valid 將會匹配該元素。這裡要注意假如我們沒有為 <input> 作約束,例如<input type="text">,那麼它的任意輸入將使元素既不會被 :valid 匹配,也不會被 :invalid 匹配。

.valid input:valid {  color: green;}.valid input:invalid {  color: red;}
<div class="valid">
  <input type="email" name="eamil_valid" value="[email protected]">
  <input type="email" name="email_invalid" value="abc"></div>

The range pseudo-classes——:in-range, :out-of-range

:in-range:out-of-range 只對有被條件約束的元素起作用,例如 <input type="number" min="1" value="1">,如果輸入數字小於 1,那麼將會被 :out-of-range 匹配,反之則是被 :in-range 匹配。在很多時候,我們需要對“髒值”做一個高亮的顯示,以前可能需要配合 JS 對值的邊界進行檢測,然後在對元素的樣式進行修改。而現在,有了這兩個偽類的存在,我們可以完全使用 CSS 來控制。

.range input:in-range {  color: green;}.range input:out-of-range {  color: red;}
<div class="range">
  <input type="number" name="range" value="1" min="1" max="10"></div>

The optionality pseudo-classes——:required, :optional

:required:optional 分別匹配帶有 required 標識的元素和不帶 required 標識的元素。同樣地,我們可以利用這兩個偽類來對需要填寫的元素新增特定的樣式。

.optionality input:required {  color: green;}.optionality input:optional {  color: red;}
<div class="optionality">
  <input type="text" name="required" value="required" required>
  <input type="text" name="optional" value="optional"></div>

The user-interaction pseudo-class——:user-error

:user-error 會匹配 :invalid, :out-of-range 和沒有任何值的 :required 元素,但是假如是初始化時就觸發這三種錯誤,user-error 將不會匹配該元素,只有當用戶和元素進行互動或者提交了該表單並且觸發了這三種錯誤,:user-error 才會被觸發。Chrome 和 Safari 可能尚未支援,所以無法驗證正確性。

.user-error input:user-error {  color: red;}.user-error input:valid {  color: green;}
<div class="user-error">
  <input type="email" name="eamil_valid" value="[email protected]">
  <input type="email" name="email_invalid" value="abc"></div>

The mutability pseudo-classes——:read-only, :read-write

:read-only 匹配不可被編輯的元素,:read-write 則匹配可被編輯的元素,例如 <input> 或者 contenteditable="true"的元素。:-moz-read-only:-moz-read-write 分別是他們的相容性寫法。

.mutability :read-only {  color: red;}.mutability :read-write {  color: green;}
<div class="mutability">
  <input type="text" name="read-write-input" value="read-write">
  <p contenteditable="true">read-write-paragraph</p>
  <p>read-only-paragraph</p></div>

The placeholder-shown pseudo-class——:placeholder-shown

:placeholder-shown 匹配 placeholder 文字顯示時的 <input> 元素。::-webkit-input-placeholder,::-moz-placeholder, :-ms-input-placeholder 分別是它在不同瀏覽器的相容性寫法。在此之前,原生的 placeholder 文字是沒有方法去改變其顏色的,大多數做法是使用 value 來代替 placeholder,同時利用 JS 對 input 的 focus 事件進行監聽,將 value 清空,從而達到一個模仿 placeholder 的效果。

.placeholder input:placeholder-shown {  color: green;}.placeholder input::-webkit-input-placeholder {  color: green;}.placeholder input::-moz-placeholder {  color: green;}.placeholder input:-ms-input-placeholder {  color: green;}
<div class="placeholder">
  <input type="text" name="placeholder" placeholder="placeholder is green"></div>

Grid-Structural Selectors

該特性將對例如 <table> 的柵格佈局起作用。它包含 :column(selector), :nth-column(n):nth-last-column(n)。目前瀏覽器都還未支援,無法實驗正確性。這些偽類將讓柵格佈局的樣式控制變得更為簡單,不過更多的試驗要等到瀏覽器支援才能一一試驗。

:column(selector)

:column(selector) 將匹配例如 <table> 中 帶有 selector 類名的那一列的所有元素。

:column(.selected) {  color: green;}
<table>
  <col class="selected" />
  <col class="blur" />
  <col class="blur" />

  <tr>
    <td>A</td>
    <td>B</td>
    <td>C</td>
  </tr>

  <tr>
    <td>D</td>
    <td>E</td>
    <td>F</td>
  </tr>

  <tr>
    <td>G</td>
    <td>H</td>
    <td>I</td>
  </tr></table>

在上面的例子中,A、D、G 都將是綠色的。

:nth-column(n) 和 :nth-last-column(n)

:nth-column(n) 匹配括號內 n 的計算值的某一列的元素,計算方式是從頭開始計算,而 :nth-last-column(n) 則是從後開始計算。

:nth-column(2n) {  color: red;}:nth-last-column(3n) {  color: green;}
<table>
  <col class="selected" />
  <col class="blur" />
  <col class="blur" />

  <tr>
    <td>A</td>
    <td>B</td>
    <td>C</td>
  </tr>

  <tr>
    <td>D</td>
    <td>E</td>
    <td>F</td>
  </tr>

  <tr>
    <td>G</td>
    <td>H</td>
    <td>I</td>
  </tr></table>

Tree-Structural pseudo-classes——:blank

Tree-Structural pseudo-classes 是 CSS3 中的規範,但在 CSS Selectors Level 4 中加入了 :blank,它和 :empty 類似,區別在於 :empty 只能匹配沒有任何內容的元素,而 :blank 可以匹配帶有 spaces(空格), tabs(縮排符)segment breaks(段落過段) 內容的元素。

Combinators——>>

A >> B 匹配祖先元素為 A 的 B元素,其用法與 A B 一樣,與 >, +, ~ 用意一樣,不過意義不同。


上面的特性都已經存在 Working Draft 中,還有一些 Editor’s Draft 的特性,也順帶一提。

The Relational Pseudo-class——:has()

:has(selector) 匹配含有 某些規則 的元素。

下面例子將匹配含有 img 子元素的 a 元素:

a:has(> img)
/* 將匹配擁有 dt 兄弟元素的 dt 元素 */dt:has(+ dt)

下面例子將匹配不含有 h1、h2、h3、h4、h5、h6 元素的 section 元素:

section:not(:has(h1, h2, h3, h4, h5, h6))

和上面例子不同,下面交換了兩個偽類的巢狀,表示匹配含有的不是 h1、h2、h3、h4、h5、h6 子元素的元素,區別在於這種寫法要求必須含有一個子元素,而上面的寫法可以不含有子元素也會被匹配:

section:has(:not(h1, h2, h3, h4, h5, h6))

The Drag-and-Drop Pseudo-class——:drop, :drop()

:drop:drop() 匹配可被放置拖動元素的目標元素,兩者區別在於 :drop() 可以匹配一些規則,包括 active, valid,invalidactive 會匹配可被放置的目標元素,valid 匹配放置的元素為合法元素的目標元素,invalid 反之。如果:drop() 括號裡沒有任何過濾,那麼將和 :drop 沒有區別。

最後

文章介紹了目前 CSS Selectors Level 4 的一些新的特性,我們看到 CSS 正在逐漸將以前需要依賴 JS 做到的事情轉化為 CSS 自身能夠處理的過程,這個將大大降低了 CSS 和 JS 之間的程式碼耦合,從而也降低了專案迭代過程中的維護成本。從 1996 年發表的 CSS1 規範至今已經過去了 20年,從瀏覽器廠商的各自為戰到現在各個瀏覽器廠商遵守規範進行 CSS 新特性的開發,可以說現在前端因為瀏覽器相容性的原因所造成的開發成本已經不如以前了,當然,現在還存在許多需要與 IE8+ 打交道的網站,但是這個比例已經大大降低了。還有新的挑戰來源於手機端 Web 的興起,手機的效能遠遠不及 PC 的效能,而國內手機瀏覽器核心的百花齊放又再一次讓相容性這個嚴峻的問題擺在了前端開發者眼前,開發者們可以在開源社群中尋找各種 polyfill 來消除這種差異,也可以利用各種 CSS hack 來解決不同瀏覽器的相容性,但是不可避免地會對效能造成影響。

其實說到了 CSS,不得不說與 CSS 密不可分的 JavaScript,JavaScript 自從 ES6 的發表,解決了 JavaScript 某些方面多年來混亂的局面,EcmaScript 的版本更新頻率也變得勤快了起來。在 ES6 中我們可以看到的是其解決很多 ES5 上存在的問題,例如非同步事件處理,其新增了許多方法我們都發現 JQuery、underscore 等知名庫的影子,所以說,開發者的熱情是推動規範的不可或缺的中堅力量。規範的制定不再是一言堂,開發者們也有了說話和建議的餘地。

無論如何,相信在各大廠商的和 W3C 工作組的推動下,未來不管在 CSS 還是 JavaScript 上,將會逐漸走向規範上的統一,讓我們拭目以待吧!