網(wǎng)頁(yè)的結(jié)構(gòu)和重疊

前面我們介紹了如何利用網(wǎng)頁(yè)文檔結(jié)構(gòu)和CSS選擇器為元素應(yīng)用各種豐富的樣式。每個(gè)合法的文檔都會(huì)生成一個(gè)結(jié)構(gòu)樹,了解了這一點(diǎn),就能根據(jù)元素的祖先、屬性、兄弟元素等等創(chuàng)建選擇器來(lái)選擇元素。有了這個(gè)結(jié)構(gòu)樹,選擇器才能起作用,這也是CSS另一個(gè)重要方面(即繼承)的核心。

繼承(Inheritance)是從一個(gè)元素向其后代元素傳遞屬性值所采用的機(jī)制。確定應(yīng)當(dāng)向一個(gè)元素應(yīng)用哪些值時(shí),網(wǎng)頁(yè)瀏覽器不僅要考慮繼承,還要考慮聲明的特殊性,另外需要考慮聲明本身的來(lái)源。這個(gè)過程就稱為層疊(cascade)。我們接下來(lái)將討論這3種機(jī)制之間的關(guān)聯(lián):特殊性、繼承和層疊。

特殊性

我們現(xiàn)在知道了可以使用多種不同的方法選擇元素。實(shí)際上,可能同一個(gè)元素可以使用兩個(gè)或多個(gè)規(guī)則來(lái)選擇,每個(gè)規(guī)則都有其自己的選擇器,下面考慮以下3對(duì)規(guī)則。假設(shè)每一對(duì)規(guī)則都匹配同樣的元素:

h1 {color: red;}

body h1 {color: green;}

h2.grape {color: purple;}

h2 {color: silver;}

html > body table tr[id="totals"] td ul > li {color: maroon;}

li#answer {color: navy;}

顯然,每一對(duì)規(guī)則中只有一個(gè)勝出,因?yàn)樗ヅ涞脑刂荒苁悄骋环N顏色(或此或彼)。那么怎么知道哪一個(gè)規(guī)則更強(qiáng)呢?

答案就在于每個(gè)選擇器的特殊性(specificity)。對(duì)于每個(gè)規(guī)則,用戶代理會(huì)計(jì)算選擇器的特殊性,并將這個(gè)特殊性附加到規(guī)則中的各個(gè)聲明。如果一個(gè)元素有兩個(gè)或多個(gè)沖突的屬性聲明,那么有最高特殊性的聲明就會(huì)勝出。

注意:這并不是解決沖突的全部。實(shí)際上,所有樣式?jīng)_突的解決都由層疊來(lái)處理,本章后面專設(shè)了一節(jié)介紹這個(gè)內(nèi)容。

選擇器的特殊性由選擇器本身的組件確定。特殊性值表述為4個(gè)部分,如:0,0,0,0。一個(gè)選擇器的具體特殊性如下確定:

對(duì)于選擇器中給定的各個(gè)ID屬性值,加0,1,0,0。

對(duì)于選擇器中給定的各個(gè)類屬性值,屬性選擇或偽類,加0,0,1,0。

對(duì)于選擇器中給定的各個(gè)元素和偽元素,加0,0,0,1。偽元素是否有特殊性,在這方面css2有些自相矛盾,不過css2.1很清楚的指出,偽元素有特殊性,而且其特殊性為0,0,0,1。

結(jié)合符和通配符選擇器對(duì)特殊性沒有任何貢獻(xiàn)(后面還會(huì)更多地介紹這些值)。

例如,以下規(guī)則中選擇器的特殊性見注釋:

h1{color:red;}/* specifity = 0,0,0,1 */

p em{color:purple;}/* specifity = 0,0,0,2 */

.grape{color:purple;}/* specifity = 0,0,1,0 */

*.bright{color:yellow;}/* specifity = 0,0,1,0 */

p.bright em.dark{color:maroon;}/* specifity = 0,0,2,2 */

#id216{color:blue;}/* specifity = 0,1,0,0 */

div#sidebar *[href]{color:silver;}/* specifity = 0,1,1,1 */

假設(shè)有以下情況,一個(gè)em元素與上例中的第2條規(guī)則匹配,又與第5條規(guī)則匹配,這個(gè)元素將是紫紅色,因?yàn)榈?條規(guī)則的特殊性高于第2條規(guī)則的特殊性。

下面做個(gè)練習(xí),回顧本節(jié)前面給出的幾組規(guī)則,看看它們有怎樣的特殊性:

h1 {color:red;}/* 0,0,0,1 */

body h1 {color:green;}/* 0,1,0,2 (winner)*/

h2.grape{color:purple;}/* 0,0,1,1 (winner)*/

h2 {color:silver;}/* 0,0,0,1 */

html > body table tr[id="totals"] td ul > li {color: maroon;}/* 0,0,1,7 */

li#answer {color: navy;}/* 0,1,0,1 (winner)*/

上面已經(jīng)指出每組規(guī)則中的勝出規(guī)則,在上述各種情況下,那些規(guī)則之所以勝出是因?yàn)槠涮厥庑愿摺R⒁馓厥庑允侨绾闻判虻?。在第二組中,選擇器h2.grape能“贏”是因?yàn)樗嗔艘粋€(gè)1: 0,0,1,1大于0,0,0,1。在第三組中,第二個(gè)規(guī)則勝出是因?yàn)?,1, 0,1大于0,0,1,7。實(shí)際上,特殊性值0,0,1,0比值0,0, 0,13更高。

之所以會(huì)這樣,是因?yàn)橹凳菑淖笙蛴遗判虻?。特殊性?,0, 0,0大于以0開頭的所有特殊性值,而不論后面的數(shù)是什么。所以0,1, 0,1比0,0,1,7高,因?yàn)榍耙粋€(gè)值中第二位上的1大于第二個(gè)值中第二位上的0。

聲明和特殊性

一旦確定一個(gè)選擇器的特殊性,這個(gè)值將授予其所有相關(guān)聲明??紤]以下規(guī)則:

h1 {color: silver; background: black;}

由于特殊性的緣故,網(wǎng)頁(yè)瀏覽器必須相應(yīng)地處理這個(gè)規(guī)則,將其“解組”為單獨(dú)的規(guī)則。因此,前面的例子將變成:

h1 {color: silver;}

h1 {background: black;}

這兩個(gè)規(guī)則的特殊性都是0, 0 , 0 , 1,各聲明得到的特殊性值也就是0, 0 , 0 , 1。分組選擇器也同樣會(huì)完成這種分解過程。給定以下規(guī)則:

h1, h2.section {color: silver; background: black;}

網(wǎng)頁(yè)瀏覽器將把它處理為:

h1 {color: silver;}/* 0,0,0,1 */

h1 {background: black;}/* 0,0,0,1 */

h2.section {color: silver;}/* 0,0,1,1 */

h2.section {background: black;}/* 0,0,1,1 */

如果多個(gè)規(guī)則與同一個(gè)元素匹配,而且有些聲明相互沖突,在這種情況下特殊性就很重要。例如,考慮以下規(guī)則:

h1 + p {color: black; font-style: italic;}/* 0,0,0,2 */

p {color: gray; background: white; font-style: normal;}/* 0,0,0,1 */

*.aside {color: black; background: silver;}/* 0,0,1,0 */

當(dāng)這些規(guī)則應(yīng)用到以下標(biāo)記時(shí),顯示的內(nèi)容將如圖3-1所示:

<h1>Greetings!</h1>

<p class="aside"> It's a fine way to start a day, don't you think?

</p>

<p> There are many ways to greet a person, but the words are not as important as the act of greeting itself.

</p>

<h1>Salutations!</h1>

<p> There is noth1ng finer than a hearty welcome from one's fellow man.

</p>

<p class="aside"> Although a thick and juicy hamburger with bacon and mushrooms runs a close second.

</p>

任何情況下,用戶代理都會(huì)確定哪些規(guī)則與一個(gè)元素匹配,計(jì)算出所有相關(guān)的聲明及其特殊性,確定哪些規(guī)則勝出,然后將勝出的規(guī)則應(yīng)用到元素,從而得到應(yīng)用樣式后的結(jié)果。每個(gè)元素、選擇器和聲明上都必須完成這些工作。幸運(yùn)的是,用戶代理會(huì)自動(dòng)完成所有這些工作。這個(gè)行為是層疊的一個(gè)重要部分,本章后面還將深入討論層疊。

通配選擇器特殊性

前面提到過,通配選擇器對(duì)一個(gè)選擇器的特殊性沒有貢獻(xiàn)。換句話說,其特殊性為0,0,0,0,這與根本沒有特殊性有區(qū)別(有關(guān)內(nèi)容將在“繼承”一節(jié)中介紹)。因此,給定以下兩條規(guī)則,div下包含的段落將是黑色,而其他元素都是灰色:

div p {color: black;}/* 0,0,0,2 */

*{color: gray;}/* 0,0,0,0 */

如你所料,這意味著如果一個(gè)選擇器中包含通配選擇器和其他選擇器,該選擇器的特殊性不會(huì)因通配選擇器的出現(xiàn)而改變。下面兩個(gè)選擇器的特殊性完全相同:

div p /* 0,0,0,2 */

body * strong /* 0,0,0,2 */

相比之下,結(jié)合符則根本沒有特殊性,甚至連0特殊性都沒有。因此,它們對(duì)選擇器的總特殊性沒有任何影響。

ID和屬性選擇器的特殊性

需要著重指出,ID選擇器和指定id屬性的屬性選擇器在特殊性上有所不同。再來(lái)看示例代碼中的第三組規(guī)則,可以看到:

html > body table tr[id="totals"] td ul > li {color: maroon;}/* 0,0,1,7 */

li#answer {color: navy;}/* 0,1,0,1 {winner}*/

第二個(gè)規(guī)則中的ID選擇器(#answer)為選擇器的總特殊性貢獻(xiàn)了0,1, 0,0。而在第一個(gè)規(guī)則中,屬性選擇器([id="totals"])只對(duì)總特殊性貢獻(xiàn)了0,0,1,0。因此,給定以下規(guī)則,id為meadow的元素將變成綠色:


#meadow {color: green;}/* 0,1,0,0 */

*[id:"meadow"]{color: red;}/* 0,0,1,0 */

內(nèi)聯(lián)樣式特殊性

到目前為止,我們已經(jīng)見過以0開頭的特殊性,所以你可能會(huì)奇怪為什么會(huì)有這些特殊性。一般地,第一個(gè)0是為內(nèi)聯(lián)樣式聲明保留的,它比所有其他聲明的特殊性都高??紤]以下規(guī)則和標(biāo)記片段:

h1 {color: red;}

<h1 style="color: green?">The Meadow Party</h1>

假設(shè)這個(gè)規(guī)則應(yīng)用到h1元素,h1的文本還將是綠色。CSS2.1中就是如此,這是因?yàn)槊總€(gè)內(nèi)聯(lián)聲明的特殊性都是1,0, 0,0。

這意味著,即使有id屬性的元素與某個(gè)規(guī)則匹配,也必須遵循內(nèi)聯(lián)樣式聲明。下面把上例修改為包括一個(gè)id屬性:

h1#meadow {color: red;}

<h1 id="meadow" style="color: green;">The Meadow Party</h1>

由于內(nèi)聯(lián)聲明的特殊性最高,h1元素的文本還是綠色。

注意:為內(nèi)聯(lián)樣式聲明保留一位,這是CSS2.1才新增的,這樣做是為了反映寫CSS2.1當(dāng)時(shí)的Web 瀏覽器表現(xiàn)。在CSS2中,內(nèi)聯(lián)樣式聲明的特殊性是1,0,0 (CSS2特殊性包含3個(gè)值,而不是4個(gè))。換句話說,它與ID選擇器的特殊性相同,所以ID選擇器很容易覆蓋內(nèi)聯(lián)樣式。

重要性

有時(shí)某個(gè)聲明可能非常重要,超過了所有其他聲明。CSS2.1稱之為重要聲明(原因顯而易見),并允許在這些聲明的結(jié)束分號(hào)之前插入!important來(lái)標(biāo)志。

p.dark {color:#333 !important; background: white;}

在此為顏色值#333加了標(biāo)志!important,而背景值white未加這個(gè)標(biāo)志。如果你希望把兩個(gè)聲明都標(biāo)志為重要,那么每個(gè)聲明都需要它自己的!important標(biāo)志:

p.dark {color:#333 !important; background: white !important;}

必須正確地放置!important,否則聲明將無(wú)效。! important總是放在聲明的最后,即分號(hào)前面。如果一個(gè)屬性的值可以包含多個(gè)關(guān)鍵詞,如font,這一點(diǎn)則尤其重要,必須將!important標(biāo)志放在聲明的最后:

p.light {color: yellow; font: smaller Times, serif !important;}

如果!important放在font聲明的任何其他位置,整個(gè)聲明都將無(wú)效,相應(yīng)地不會(huì)應(yīng)用其任何樣式。

標(biāo)志為!important的聲明并沒有特殊的特殊性值,不過要與非重要聲明分開考慮。實(shí)際上,所有!important聲明會(huì)分組在一起,重要聲明的特殊性沖突會(huì)在重要聲明內(nèi)部解決,而不會(huì)與非重要聲明相混。類似地,我們認(rèn)為所有非重要聲明也歸為一組,使用特殊性來(lái)解決沖突。如果一個(gè)重要聲明和一個(gè)非重要聲明沖突,勝出的總是重要聲明。圖3-2展示了以下規(guī)則和標(biāo)記片段的結(jié)果:

h1 {font-style: italic; color: gray !important;}

.title {color: black; background: silver;}

*{background: black !important;}

<h1 class="title">NightWing</h1>