CSS是對網頁設計師可用的最強大的工具之一。使用它我們可以在幾分鐘內改變一個網站的介面,而不用改變頁面的標籤。但是儘管事實上,我們每個人也都意識到了它是有用的,CSS 選取器遠未發揮它們的潛力,有的時候我們還趨向於使用過多的和無用的class、id、div、span等把我們的HTML搞的很淩亂。
避免讓這些“瘟疫”在你的標籤中傳播並保持其簡潔和語義化的最佳方式,就是使用更複雜的CSS選取器,它們可以定位於指定的元素而不用使用額外的class或id,而且通過這種方式也可以讓我們的代碼和樣式更加靈活。
CSS的優先順序
在深入研究進階CSS選取器領域之前,理解CSS優先順序是如何工作的是很重要的,這樣我們就知道如何適當的使用我們的選取器並避免浪費大量的時間來調試一些只要我們注意到優先順序的話就很容易被搞定的問題
當我們寫CSS的時候我們必須注意有些選取器在級聯(cascade)上會高於其它選取器,我們寫在最後面的選取器將不一定會覆蓋前面我們寫在同一個元素的樣式。
那麼你如何計算指定選取器的優先順序?如果你考慮到將優先順序表示為用逗號隔開的四個數字就會相當簡單,比如:1, 1, 1, 1 或0, 2, 0, 1
第一個數字(a)通常就是0,除非在標籤上使用style屬性;
第二個數字(b)是該選取器上的id的數量的總和;
第三個數字(c)是用在該選取器上的其它屬性選取器和偽類的總和。這裡包括class (.example) 和屬性選取器(比如 li[id=red]);
第四個數字(d)計算元素(就像table、p、div等等)和虛擬元素(就像:first-line等);
通用選取器(*)是0優先順序;
如果兩個選取器有同樣的優先順序,在樣式表中後面的那個起作用。
讓我們看幾個例子,這樣或許比較容易理解些:
#sidebar h2 — 0, 1, 0, 1
h2.title — 0, 0, 1, 1
h2 + p — 0, 0, 0, 2
#sidebar p:first-line — 0, 1, 0, 2
在下面的例子中,第一個將會起作用,因為它比第二個優先順序高:
#sidebar p#first { color: red; } — 0, 2, 0, 1
#sidebar p:first-line { color: blue; } — 0, 1, 0, 2
至少基本理解優先順序是如何工作的是很重要的,但是一些工具比如Firebug,在我們審查指定元素的時候,按照選取器的優先順序列出所有的css選取器對讓我們知道在指定元素上哪個選取器是有效是很有用的。
讓你非常容易的看到那個選取器作用於一個元素上了。
1. 屬性選取器
屬性選取器(Attribute selector)讓你可以基於屬性來定位一個元素。你可以只指定該元素的某個屬性,這樣所有使用該屬性——而不管它的值——的這個元素都將被定位,也可以更加明確並定位在這些屬性上使用特定值的元素 —— 這就是屬性選取器展示它們的威力的地方。
有6個不同類型的屬性選取器:
[att=value]
該屬性有指定的確切的值。
[att~=value]
該屬性的值必須是一系列用空格隔開的多個值,(比如,class=”title featured home”),而且這些值中的一個必須是指定的值”value”。
[att|=value]
屬性的值就是“value”或者以“value”開始並立即跟上一個“-”字元,也就是“value-”。(比如lang=”zh-cn”)
[att^=value]
該屬性的值以指定值開始。
[att$=value]
該屬性的值包含指定的值(而無論其位置)。
[att*=value]
該屬性的值以指定的值結束
比如,如果你想要改變你的部落格上的日誌部分元素的背景顏色,你可以使用一個指定每一個class屬性值以“post-”開始的div的屬性選取器:
div[class|=“post”] {
background-color: #333;
}
該語句將匹配所有class屬性包含”post”並帶”-”字元的div元素。(註:此處英文原文有些上下文不對應,我已經更正——譯者,神飛)
屬性選取器的另一個很有用的用處是定位不同類型的input元素。比如,如果你想讓你的文本輸入框使用一個特定的寬度,你可以使用下面的屬性選取器:
input[type=“text”] {
width: 200px;
}
這將匹配所有type屬性值等於“text”的input元素。
現在我們可能像為網站上不同的檔案類型的連結添加不同的表徵圖,這樣你的網站的訪客就會知道他們將獲得一個圖片、或者PDF檔案、或者是一個word文檔等。這就可以使用屬性選取器來實現:
a[href*=“.jpg”] {
background: url(jpeg.gif) no-repeat left 50%;
padding: 2px 0 2px 20px;
}
a[href*=“.pdf”] {
background: url(pdf.gif) no-repeat left 50%;
padding: 2px 0 2px 20px;
}
a[href*=“.doc”] {
background: url(word.gif) no-repeat left 50%;
padding: 2px 0 2px 20px;
}
在此例中,我們使用了一個定位所有的連結(a)的href屬性分別以.jpg,.pdf或.doc結束(*)的屬性選取器。詳細可查看前端觀察之前的一篇文章《使用CSS選取器建立個人化連結樣式》
瀏覽器支援
除了Internet Explorer 6,所有的主流瀏覽器都支援屬性選取器。這意味著,如果你在你的網站上使用了屬性選取器,你應該確保IE6使用者將仍能獲得一個可用的網站。比如我們的第三個例子,為連結添加表徵圖可以給你的網站帶來另一個層級的可用性,但是如果這些連結不顯示任何表徵圖,該網站仍然還是可用的。
2. 子選取器
子選取器用符號“》”表示。它允許你定位某個元素的第一級子項目。
比如,如果你想匹配所有的作為你的網站側欄的div的子項目的h2元素,而不是可能是在div元素內的所有h2元素,也不是div的孫元素(或者更低層級的),你就可以使用下面的選取器:
div#sidebar > h2 {
font-size: 20px;
}
你當然也可以同時使用子項目和後代元素。比如,如果你想定位只有在body元素的子級div元素內的blockquote元素(比如你想匹配主要的div內的blockquotes,而不是其它部分的:
body > div > div blockquote {
margin-left: 30px;
}
瀏覽器支援
和屬性選取器一樣,子選取器不被IE6支援。如果你通過這種方式要實現的效果和網站的可用性或總體設計息息相關,你可以考慮使用一個class,或者使用只針對IE6的樣式,但是這將有悖使用子選取器的目的。
3. 兄弟組合
有兩類兄弟組合:臨近兄弟組合和普通兄弟組合
臨近兄弟組合
該選取器使用加號“+”來連結前後兩個選取器。選取器中的元素有同一個父親,而且第二個必須緊緊的跟著第一個。
臨近兄弟組合可以灰常有用,比如,在處理文字的時候。比如我們想對段落後面的h2標題添加一個頂間距“margin-top”(當然或許你無需為h1標籤後面的h2添加,或者它可能是頁面的第一個元素):
p + h2 {
margin-top: 10px;
}
你甚至可以更加具體,你想定位某個特定的div後面的h2:
div.post p + h2 {
margin-top: 10px;
}
或者你可以搞得更加複雜:頁面的第一個段落的第一行字母顯示為小寫字母。
.post h1 + p:first-line {
font-variant: small-caps;
}
因為可能大部分日誌/文章的第一個段落都會緊緊的在H1標籤的後面。你可以在你的選取器中藉助於h1標籤。
普通兄弟組合
普通兄弟組合和臨近兄弟組合的工作原理很像,不同的是第二個選擇其無需緊緊跟隨第一個。
如果你需要定位所有的某個特定的div裡面並且跟在h1標籤後面的p標籤的話(你可能想要這些p標籤比擬的日誌的標題前面的文字大些),你就可以使用這個選取器:
.post h1 ~ p {
font-size: 13px;
}
註:這兩個選取器太像了,所有不是很好理解,可以試一試下面的這個例子:
CSS:
p + h2{color:red;
}
p ~ h2{font-weight:700;
}
HTML:
《p》咳咳,內容。《/p》
《h2》標題1《/h2》
《h2》標題2《/h2》
看看第二個h2的顏色就可以理解了吧?臨近兄弟組合只能匹配第一個選取器後面的第一個兄弟選擇其,普通兄弟組合則能匹配所有。註:該部分英文原文沒有,是神飛翻譯時自己添加。
瀏覽器支援
Internet Explorer 6依然不能理解兄弟選取器,但是,另一種情況,如果你的使用者中只有一小部分是IE6使用者,而且網站的結構不會被破壞或者受到嚴重影響,這是實現很多很酷的效果而無需在你的HTML代碼中添加無用的class或id。
4. 偽類
動態偽類
之所以被稱為動態偽類是因為它們並不存在於HTML中——而是只有當使用者和網站互動的時候才會呈現。
有兩類動態偽類:連結 和使用者行為。連結就是:link 和:visited,而使用者行為包括:hover、:active 和:focus。
在本文中提到的css選取器中,這幾個應該是最常用到的。
:link偽類用於連結尚未被使用者訪問的時候,而:visited 偽類用於使用者訪問過的連結,也就是說它們是相反的。
:hover偽類用於使用者移動他們的滑鼠在元素上,而尚未觸發或點擊它的時候。:active偽類應用於使用者點擊元素的情況。最後,:focus偽類用於元素成為焦點的時候——最常用於表單元素。
你可以在你的樣式表中使用多種使用者行為動態偽類,這樣你就可以,比如,根據使用者的滑鼠只是滑過或懸停的時候,為一個輸入框定義不同的背景色:
input:focus {
background: #D2D2D2;
border: 1px solid #5E5E5E;
}
input:focus:hover {
background: #C7C7C7;
}
瀏覽器安全色性
動態偽類被所有的現代瀏覽器支援,甚至IE6,但是請注意,對於IE系列瀏覽器來說,IE6隻允許:hover 偽類應用於連結元素(a標籤)而且只有IE8接受:active狀態。
:first-child
:first-child偽類允許你定位某個元素第一個子項目。比如,如果你想給你的無須列表的第一個li添加一個margin-top,你就可以這樣寫:
ul > li:first-child {
margin-top: 10px;
}
讓我們來看一看另一個例子:比如你想讓你的部落格的側欄的H2標籤都有個頂部邊距,以將標題和它們前面的內容區分開來,但是第一個h2不需要,你就可以使用下面的代碼:
#sidebar > h2 {
margin-top: 10px;
}
#sidebar > h2:first-child {
margin-top: 0;
}
瀏覽器安全色性
IE6 不支援 :first-child 偽類。根據偽類應用到的設計的不同,它或許不會成為關注的主要問題。比如,如果你使用:first-child 選取器來移除標題或段落上的頭部或底部的間距,你的布局在IE6中不會壞掉,它只會看起來有些不同。但是如果你使用:first-child選取器從一個,比如浮動元素,移除左邊距或右邊距,將會讓你的設計亂掉。
語言偽類
語言偽類:lang(),允許你匹配一個基於它的語言的元素。
你如,你想讓你的網站的某個特定的連結根據頁面的語言有不同的背景顏色:
:lang(en) > a#flag {
background-image: url(english.gif);
}
:lang(fr) > a#flag {
background-image: url(french.gif);
}
這個選取器將會匹配相關的連結——如果頁面的語言等於“en”或“fr”,或者以“en”或“fr”開頭並在後面帶個連字號“-”的話。
瀏覽器安全色性
不幸的是,只有IE瀏覽器中只有IE8支援該選取器,其它的主要瀏覽器都支援該偽類別選取器。
5. CSS 3 偽類
:target
當你使用錨點(片段標識符 fragment identifier)的時候,比如,http://www.smashingmagazine.com/2009/08/02/bauhaus- ninety-years-of-inspiration/#comments,這“#comments”就是一個片段標識符,你就可以使用: target偽類來控制目標的樣式。
舉個例子,比如你有一個很長的使用了很多文字和h2標題的頁面,然後在頁面的頭部有一個對這些標題的索引。如果在點擊索引內的某個連結時,相應的標題以某種方式高亮,然後滾動到相應的位置,對讀者就會很有用。很簡單。
h2:target {
background: #F2EBD6;
}
瀏覽器安全色性
這一次,IE瀏覽器完全不支援:target偽類,另一個小問題就是Opera 在使用“前進”和後退按鈕時不支援該選取器。但是其它的各個主流瀏覽器都支援該選取器。
UI元素狀態偽類
有些HTML元素有enable 或disabled 狀態(比如,文本輸入框)和 checked 或unchecked 狀態(選項按鈕和複選框)。這些狀態就可以使用:enabled、:disabled 或:checked 偽類來分別定位。
那麼你就會想,如果任意一個禁用的(disabled)文字框應該使用淺灰色的背景和虛線邊框:
input:disabled {
border:1px dotted #999;
background:#F2F2F2;
}
你也可能會想讓所有選中的複選框有個左邊距(這樣就可以在眾多的複選框中很容易認出來):
input[type=”checkbox”]:checked {
margin-left: 15px;
}
瀏覽器安全色性
所有的主流瀏覽器,除了我們常常不報希望的IE系列瀏覽器,都支援UI元素狀態偽類。如果你考慮只是添加附加層級的細節和增強網站的可用性,這個仍然是可以採用的。
6. CSS 3 結構偽類
:nth-child
:nth-child()偽類允許你定位某個父級元素的一個或多個特定的子項目。
你可以通過定義它的值為一個整數來定位某個單個子項目:
ul li:nth-child(3) {
color: red;
}
這將會讓ul元素的第三個li元素的文字變成紅色。注意如果在ul裡面有個其它類型的元素(不是li),它也會算作其子項目。
你可以使用運算式來定位子項目。比如,下面的運算式將從第四個開始匹配每第三個元素。
ul li:nth-child(3n+4) {
color: yellow;
}
在上面的這個例子中,第一個黃色的li元素將會是第四個。如果也想從第一個開始匹配,你可以使用一個簡單的運算式:
ul li:nth-child(3n) {
color: yellow;
}
這樣的話,第一個黃色的li元素將會是第三個子項目,然後是它後面的每隔第三個。現在想象一下如果你只想匹配列表中的前四個子項目:
ul li:nth-child(-n+4) {
color: green;
}
:nth-child的值同樣也可以被定義為“even” 或“odd”,和“2n” (第偶數個) 或“2n+1” (第奇數個)的效果是一樣的。
:nth-last-child
:nth-last-child偽類基本上和:nth-child偽類的作用相同,但是它從最後一個元素開始算。
使用上面的一個例子來看看:
ul li:nth-child(-n+4) {
color: green;
}
不是匹配最前面的四個li元素,該選取器匹配最後面的四個元素。
你同樣可以使用“even” 或“odd”只,同樣與nth-child不同的是,這是從最後面的元素開始算的:
ul li:nth-last-child(odd) {
color: grey;
}
:nth-of-type
:nth-of-type偽類和:nth-child也很像,不同的是它只計算選取器中指定的那個元素。
這對定位元素中包含大量不同的元素的情況會很有用。比如,我們想控制一個文字區塊中的每隔一個段落,但是我們又想要無視其它元素比片和引用塊:
p:nth-of-type(even) {
color: blue;
}
你也可以使用一些值,就像在:nth-child中使用的一樣。
:nth-last-of-type
你能猜到它吧?!:nth-last-of-type 偽類可以像前面提到的:nth-last-child一樣使用,但是這次,它將之匹配你在選取器中指定的元素類型:
ul li:nth-last-of-type(-n+4) {
color: green;
}
我們可以更加的聰明一些,在一個大的塊級選取器中結合多種這樣的偽類。比如我們想讓文章中的所有的圖片左浮動,除了第一個和最後一個(我們可以假設他們是滿寬的,無須浮動):
.post img:nth-of-type(n+2):nth-last-of-type(n+2) {
float: left;
}
所以在這個選取器的第一部分,我們從第二個圖片開始定位每一個圖片。在第二部分中,我們定位所有的圖片,除了最後一個。因為這兩個選取器並非互斥的,我們可以同時使用它們,這樣就可以一下子排除第一個和最後一個元素!
:last-child
:last-child偽類的作用類似於:first-child 偽類,但是會定位父級元素的最後一個子項目。
讓我們假設你不想讓你的日誌的div的最後一個段落也有一個底部邊距:
.post > p:last-child {
margin-bottom: 0;
}
該選取器將定位class為”post”的元素的最後一個直接子級段落。
:first-of-type 和:last-of-type
:first-of-type 偽類用於定位一個父級元素下的第一個同類子項目。
比如,你可以定位某個特定的div的第一個子級段落(p),並讓其第一行字母大寫:
.post > p:first-of-type:first-line {
font-variant: small-caps;
}
在這個選取器中,你可以確定你是在只定位class為”post”的元素的直接子級p元素,而且還是匹配第一個子級p元素。
:last-of-type偽類與此類似,只是匹配最後一個子項目。
nly-child
nly-child偽類表示一個元素是它的父級元素的唯一一個子項目。
比如說,你有一些盒子(“news”),裡面有一些文欄位落,當你有多於一個段落的時候,你想讓文字比只有一個段落的時候小一些:
div.news > p {
font-size: 1.2em;
}
div.news > p:only-child {
font-size: 1.5em;
}
第一個選取器中,我們定義”news”div的所有子級p元素的字型大小。在第二個中,我們覆蓋之前的字型大小,如果該p元素是“news” div的唯一子項目的時候,它的字型大小會比較大一些。
nly-of-type
nly-of-type偽類表示一個元素是它的父級元素的唯一一個相同類型的子項目。
這有用什麼用?假設你有一些日誌,每一篇都有個class為”post”的div,他們中的一些有多於一張圖片,但是有些可能就只有一張圖片。你想讓後者中的圖片水平置中,而在其它的有多於一張圖片的日誌中,就將它左浮動。這個需求用這個選取器就很容易實現:
.post > img {
float: left;
}
.post > img:only-of-type {
float: none;
margin: auto;
}
:empty
:empty偽類表示一個元素裡面沒有任何內容。
這個選取器可以用很多種用途。比如,你在你的“sidebar”中有若干個盒子,但是不想讓空盒子顯示出來:
#sidebar .box:empty {
display: none;
}
注意,就算”box”div裡面只有一個空格,它也不會被css當作空標籤的,這樣就不能匹配該選取器了。
瀏覽器支援
Internet Explorer (直到8.0版本)都不支援結構偽類。Firefox、Safari 和Opera 在其最新版本的瀏覽器中指出這些選取器。這意味著,使用這些選取器對網站的可用性和可訪問性是很有用的,或者如果網站的使用者中的大部分是使用IE而且你不想在某些細節上無視他們,最好還是保持使用通用的class和簡單的選取器來迎合這些選取器。否則你會被搞瘋的!
7. 否定選取器
否定選取器:not(),可以讓你定位不匹配該選取器的元素
比如,如果你需要定義表單元素中的input元素,但是又不想包括submit類型的input的時候會灰常有用——你想它們有不同的樣式,以看起來像按鈕:
input:not([type=“submit”]) {
width: 200px;
padding: 3px;
border: 1px solid #000000;
}
另一個例子,你想你的日誌的div中的所有段落(p)有比較大的字型,除了表示時間和日期的段落:
.post p:not(.date) {
font-size: 13px;
}
你可以想象這個選取器能帶給你的潛力了吧,你能夠從你的CSS檔案中剝離(剔除)的無用的大量選取器也被它廣泛支援嗎?
瀏覽器支援
Internet Explorer在這裡常常是讓我們感到掃興的東西:一點都不支援,甚至在IE8中。這大概意味著這些選取器將仍不得不等到一些開發人員開始不再顧慮將它添加到他們的樣式表中才會普及。
8. 虛擬元素
虛擬元素允許你操作HTML中不是真實存在的元素,比如一個文字區塊的第一行或者第一個字母。
虛擬元素在CSS 2.1中就已經存在,但是CSS 3說明書表示他們應該使用雙冒號“::”,以與偽類區分開來。在CSS 2.1中,他們也是使用單個冒號“:”的。瀏覽器會將能夠接受兩種格式,除非這些虛擬元素只存在於CSS3中。
::first-line
::first-line虛擬元素將匹配block、inline-block、table-caption、table-cell等等層級元素的第一行
這對在你的文字塊上添加一些微妙的排版細節相當有用,比如,將一片文章的第一行文字改成小寫字母:
h1 + p::first-line {
font-variant: small-caps;
}
如果你專心的閱讀了我們前面的內容,你將會瞭解到上面的文法意味著,緊緊的跟在H1標籤之後(+)的段落會將其第一行文字顯示為小寫字母。
你也可以針對相關的div的第一行,而不用針對實際的段落標籤(p):
div.post p::first-line { font-variant: small-caps; }
或者更進一步,定位某個特低的div的第一個段落的第一行:
div.post > p:first-child::first-line {
font-variant: small-caps;
}
這裡,“》”符號表示你指定的是post div的直接子級元素,這樣如果段落被包括在第二級div中,它就不會匹配這個選取器。
::first-letter
::first-letter虛擬元素將會匹配一個文字區塊的第一個字母,除非在同一行裡麵包含一些其它元素,比片。
和::first-line偽類一樣,::first-letter通常用於給文本元素添加排版細節,比如下沉字母或首字母。
這裡是如何使用::first-letter虛擬元素建立首字下沉的例子:
p {
font-size: 12px;
}
p::first-letter {
font-size: 24px;
float: left;
}
注意如果你在某些元素中同時使用::first-line 和::first-letter ,::first-letter 屬性將覆蓋從::first-line中繼承下來的某些屬性。
如果你不知道W3C規則的話,這個元素有時會產生意想不到的結果:它事實上是使用最長的規則的選取器!所以如果你計劃使用它的話最好仔細的閱讀一下 (其它選取器也一樣)。
::before 和 ::after
::before和::after 虛擬元素用於在一個元素的前面或後面插入內容,純CSS方法。
這些元素將繼承它們將附加的元素的大部分屬性。
假設你想在你的頁面中的表徵圖的描述前面添加文字“Graphic number x:”。你將無需寫文字“Graphic number”,或者自己手動添加數字:
.post {
counter-reset: image;
}
p.description::before {
content: “Figure number ” counter(image) “: ”;
counter-increment: image;
}
那麼這會產生什嗎?
首先,我們告訴HTML來建立“image”計數器。比如我們可以添加該屬性到頁面的body。同樣我們也可以給該計數器起任何一個名字,只要你想,只要我們常常使用同樣的名字引用它:自己試試吧!
那麼我們想在class為”description”的每一個段落之前添加這一塊內容: “Figure number ” — 注意只有我們在引號裡面寫的內容才會被建立到頁面中,所以我們也要添加一個空格!
然後,我們就有了counter(image):這將用到我們之前在.post選取器中定義的屬性。它預設會從數字1開始。
下一個屬性在那裡表示計數器知道對於每一個p.description,它需要將image計數器增加1 (counter-increment: image)。
它並不像看起來的那麼複雜,而且還會灰常的有用。
::before和::after虛擬元素常常只使用content屬性,來添加一些短語或排版元素,但是這裡我們展示了我們如果以一種更加強大的結合counter-reset和counter-increment屬性的方式來使用它們。
有趣的是: ::first-line 和::first-letter 虛擬元素可以匹配使用::before虛擬元素產生的內容,如果存在的話。
瀏覽器支援
如果使用單個冒號的話(比如, :first-letter, 而不是::first-letter),這些虛擬元素被IE8支援(但是不被IE7或6支援)。但是左右其他的主流瀏覽器都支援這些選取器。
結語
乏味的講述終於結束了,現在該你來領悟本文的要義並自己嘗試了: 開始通過建立實驗性的頁面並測試所有的這些選取器,在有疑問的時候返回這裡並確保總是遵循W3C的規則,但是請不要只是坐在那裡想這些選取器尚未被完全支援你就無視它們。
如果你敢於冒險,或者你不害怕放棄之前的遍地無用和非語義化的class和id,為什麼不嘗試一兩個這些強大的CSS選取器到你的下一個項目中呢?我們保證你不會回頭的!