前端er們大都或多或少地接觸過CSS偽類和偽元素,比如最常見的:focus
,:hover
以及<a>
標簽的:link
、visited
等,偽元素較常見的比如:before
、:after
等。
其實上面提到的這些偽類和偽元素都是CSS1和CSS2中的概念,CSS1和CSS2中對偽類的偽元素的區別比較模糊,甚至經常有同行將:before
、:after
稱為偽類。CSS3對這兩個概念做了相對較清晰地概念,並且在語法上也很明顯的講二者區別開。
首先看看CSS2中對偽類的定義:
CSS 偽類用於向某些選擇器添加特殊的效果。
單單看定義完全不懂在講什麼。截止CSS2,偽類有以下幾種(偷個懶,截圖引自W3School):
然後是CSS3對偽類的定義:
The pseudo-class concept is introduced to permit selection based on information that lies outside of the document tree or that cannot be expressed using the other simple selectors.
A pseudo-class always consists of a "colon" (:) followed by the name of the pseudo-class and optionally by a value between parentheses.
Pseudo-classes are allowed in all sequences of simple selectors contained in a selector. Pseudo-classes are allowed anywhere in sequences of simple selectors, after the leading type selector or universal selector (possibly omitted). Pseudo-class names are case-insensitive. Some pseudo-classes are mutually exclusive, while others can be applied simultaneously to the same element. Pseudo-classes may be dynamic, in the sense that an element may acquire or lose a pseudo-class while a user interacts with the document.
簡單翻譯一下:
偽類存在的意義是為了通過選擇器找到那些不存在與DOM樹中的信息以及不能被常規CSS選擇器獲取到的信息。
偽類由一個冒號
:
開頭,冒號後面是偽類的名稱和包含在圓括號中的可選參數。
任何常規選擇器可以再任何位置使用偽類。偽類語法不區別大小寫。一些偽類的作用會互斥,另外一些偽類可以同時被同一個元素使用。並且,為了滿足用戶在操作DOM時產生的DOM結構改變,偽類也可以是動態的。
其實第一段話就囊括CSS3偽類的全部定義了,這段話中指出CSS3偽類的功能有兩種:
<a>
標簽的:link
、visited
等,這些信息不存在與DOM樹結構中,只能通過CSS選擇器來獲取;:target
,它的作用是匹配文檔(頁面)的URI中某個標志符的目標元素,例如我們可以通過如下代碼來實現頁面內的區域跳轉:<ul class="tabs">
<li><a href="#tab1">標簽一</a></li>
<li><a href="#tab2">標簽二</a></li>
<li><a href="#tab3">標簽三</a></li>
</ul>
<div id="tab1" class="tab_content">
<!--tabed content--></div>
<div id="tab2" class="tab_content">
<!--tabed content--></div>
<div id="tab3" class="tab_content">
<!--tabed content--></div>
CSS代碼如下:
.tab_content {
height: 800px;
background: red;
margin-bottom: 100px;
}
#tab1:target, #tab2:target, #tab3:target {
background:blue;
}
當然,通過JavaScript來獲取window.location.hash
同樣可以實現上例中的效果,但這是另外一回事了。總之,:target
通過CSS實現了常規CSS無法實現的邏輯。
其實對比來看,CSS2中對偽類的定義也是合理地,但是它並未指出“某些選擇器”是“哪些選擇器”,CSS3對偽類的定義就顯得明確了很多。
再舉個栗子,通過:nth-child()
偽類可以實現一些很有意思的效果,比如:
table tr:nth-child(2n) td{
background-color: #ccc;
}
table tr:nth-child(2n+1) td{
background-color: #fff;
}
table tr:nth-child(2n+1):nth-child(5n) td{
background-color: #f0f;
}
上面的代碼將所有偶數行背景色設置為#ccc
,不能被5整除的奇數行設置背景色#fff
,能夠被5整除的奇數行設置背景色#f0f
。
如果不使用偽類而是使用JavaScript代碼來實現上述的效果,恐怕要復雜很多。
可以總結出:nth-child()
偽類的效果是將被常規css選擇器篩選出的元素按照既定規定進行再次篩選。
CSS3中還引入了許多新的偽類,感興趣的讀者可以參考這裡。
CSS2中對偽元素的定義:
CSS 偽元素用於向某些選擇器設置特殊效果。
好吧,跟偽類的定義完全一樣有木有(吐槽一下W3School的翻譯)。其實人家這樣翻譯也沒有錯,本來CSS2對偽類和偽元素的定義就是完全一樣的:
CSS introduces the concepts of pseudo-elements and pseudo-classes to permit formatting based on information that lies outside the document tree.
截止CSS2,偽元素有以下幾種:
然後再看CSS3中偽元素的定義:
Pseudo-elements create abstractions about the document tree beyond those specified by the document language. For instance, document languages do not offer mechanisms to access the first letter or first line of an element's content. Pseudo-elements allow authors to refer to this otherwise inaccessible information. Pseudo-elements may also provide authors a way to refer to content that does not exist in the source document (e.g., the ::before and ::after pseudo-elements give access to generated content).
A pseudo-element is made of two colons (::) followed by the name of the pseudo-element.
This :: notation is introduced by the current document in order to establish a discrimination between pseudo-classes and pseudo-elements. For compatibility with existing style sheets, user agents must also accept the previous one-colon notation for pseudo-elements introduced in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and :after). This compatibility is not allowed for the new pseudo-elements introduced in this specification.
Only one pseudo-element may appear per selector, and if present it must appear after the sequence of simple selectors that represents the subjects of the selector.
Note: A future version of this specification may allow multiple pseudo-elements per selector.
簡單翻譯一下:
偽元素在DOM樹中創建了一些抽象元素,這些抽象元素是不存在於文檔語言裡的(可以理解為html源碼)。比如:documen接口不提供訪問元素內容的第一個字或者第一行的機制,而偽元素可以使開發者可以提取到這些信息。並且,一些偽元素可以使開發者獲取到不存在於源文檔中的內容(比如常見的
::before
,::after
)。
偽元素的由兩個冒號
::
開頭,然後是偽元素的名稱。
使用兩個冒號
::
是為了區別偽類和偽元素(CSS2中並沒有區別)。當然,考慮到兼容性,CSS2中已存的偽元素仍然可以使用一個冒號:
的語法,但是CSS3中新增的偽元素必須使用兩個冒號::
。
一個選擇器只能使用一個偽元素,並且偽元素必須處於選擇器語句的最後。
注:不排除未來會加入同時使用多個偽元素的機制。
同樣,第一段話是偽元素的清晰定義,也是偽元素與偽類最大的區別。簡單來說,偽元素創建了一個虛擬容器,這個容器不包含任何DOM元素,但是可以包含內容。另外,開發者還可以為偽元素定制樣式。
已::first-line
為例,它獲取了指定元素的第一行內容並且將第一行的內容加入到虛擬容器中。如果通過JavaScript來實現這個邏輯,那麼要考慮的因素就太多了,比如制定元素的寬度、字體大小,甚至浮動元素的圖文混排等等。當然,這些問題確實是可以用JavaScript來解決的,但是相對於::first-line
簡簡單單的幾個字,用JavaScript恐怕不止這些吧!
舉個綜合使用偽類和偽元素的栗子:
q:lang(de)::after{
content: " (German) ";
}
q:lang(en)::after{
content: " (English) ";
}
q:lang(fr)::after{
content: " (French) ";
}
q:not(:lang(fr)):not(:lang(de)):not(:lang(en))::after{
content: " (Unrecognized language) ";
}
以上代碼通過偽類"lang
獲取不同lang
屬性的節點,並為之設置偽元素::after
,偽元素的內容是此節點的語言類型。
最後,總結一下偽類與偽元素的特性及其區別: