前言
上一篇系列文章整理了CSS選擇器的基礎使用方法,因為內容較多且細緻,寫了很多DEMO,目前將它整理成適合移動端瀏覽器的CSS選擇器的參考手冊,方便學習CSS的人蔘考使用,馬上就要搞定了,之後會放出 (笑臉)。
本節內容會跟著上一節的內容繼續完善,首先會補充CSS選擇器的瀏覽器支援情況(主要是說IE),比如我們最常用的s1,s2,…,sN
群組選擇器在IE7時才被支援,並且IE7還支援了很多我們沒有想到的選擇器,如子元素選擇器,屬性選擇器,瞭解後你會發現IE7還是挺了不起的。
之後還會補充平常使用選擇器遇到的一些問題以及解決方案,最後瞭解瀏覽器是如何讀取選擇器的,怎樣使用選擇器能達到高效率。
瀏覽器支援
了不起的IE7
當我們在開發網頁時,如果網頁需要相容IE6,那麼自然地會把IE6和IE7瀏覽器歸為一路貨色,對於不相容的選擇器和屬性都將不再考慮使用,可是你是否知道IE7相比IE6增加了許多選擇器可以用,如群組選擇器,相鄰選擇器,兄弟選擇器,屬性選擇器。
以下選擇器是不支援IE6,僅支援 IE7 及以上的瀏覽器
基本選擇器
選擇器 | 描述 | 版本 |
---|---|---|
s1,s2,…,sN | 群組選擇器,同時匹配所有s1元素或s2元素 | 2.1 |
E > F | 子元素選擇器,匹配所有E元素的子元素F | 2.1 |
E + F | 毗鄰元素選擇器,匹配所有緊隨E元素之後的同級元素F | 2.1 |
E ~ F | 匹配任何E標籤之後的同級F標籤 | 3 |
屬性選擇器
選擇器 | 描述 | 版本 |
---|---|---|
E[attr] | 匹配att屬性的E元素 | 2.1 |
E[attr=”val”] | 匹配att屬性且屬性值等於val的E元素 | 2.1 |
E[attr~=”val”] | 匹配att屬性且屬性值中的詞列表有一個等於val的E元素 | 2.1 |
E[attr^=”val”] | 匹配att屬性且屬性值為以val開頭的字串的E元素 | 3 |
E[attr$=”val”] | 匹配att屬性且屬性值為以val結尾的字串的E元素 | 3 |
E[attr*=”val”] | 匹配att屬性且屬性值為包含val的字串的E元素 | 3 |
E[att|=”val”] | 匹配att屬性且屬性值為以val開頭並用連線符”-“分隔的字串的E元素 | 2.1 |
在IE7瀏覽器,單核取方塊的checked
在屬性選擇器中是不被支援的,這部分內容會在下面的常見問題中詳細說明。
偽類選擇器
選擇器 | 描述 | 版本 |
---|---|---|
E:hover | 設定元素在其滑鼠懸停時的樣式 | 2.1 |
E:first-child | 匹配父元素的第一個子元素E | 2.1 |
E:hover
在IE6中只有a元素可用
偽元素選擇器
選擇器 | 描述 | 版本 |
---|---|---|
E:first-letter | 選擇文字塊的第一個字母 | 2.1 |
E:first-line | 選擇元素的第一行 | 2.1 |
平庸的IE8瀏覽器
雖然來到IE8的時代,但是對於新選擇器的支援並不多,不過還好我們最常用的E:before
和E:after
配合content
屬性都在IE8中得到了很好的支援。
以下選擇器不支援IE6,IE7,僅支援 IE8 及以上的瀏覽器
偽類選擇器
選擇器 | 描述 | 版本 |
---|---|---|
E:focus | 設定物件在成為輸入焦點時的樣式 | 2.1 |
偽元素選擇器
選擇器 | 描述 | 版本 |
---|---|---|
E:before | 在元素前面插入內容,配合”content”使用 | 2.1 |
E:after | 在元素後面插入內容,配合”content”使用 | 2.1 |
狂拽炫酷*炸天的IE9
IE最好的時代就是迎接CSS3的到來,從IE9支援了一大坨新CSS3的偽類以及偽元素,我就勉強給IE使用上這個酷炫點的修飾語。
以下選擇器不支援IE6,IE7,IE8,僅支援 IE9 及以上的瀏覽器
偽類選擇器
選擇器 | 描述 | 版本 |
---|---|---|
E:checked | 匹配使用者介面上處於選中狀態的元素E | 3 |
E:enabled | 匹配使用者介面上處於可用狀態的元素E | 3 |
E:disabled | 匹配使用者介面上處於禁用狀態的元素E | 3 |
E:root | 匹配文件的根元素,對於HTML文件,就是HTML元素 | 3 |
E:last-child | 匹配父元素的最後一個子元素E | 3 |
E:nth-last-child(n) | 匹配父元素的倒數第n個子元素E | 3 |
E:nth-of-type(n) | 匹配同型別中的第n個同級兄弟元素E | 3 |
E:nth-last-of-type(n) | 匹配同型別中的倒數第n個同級兄弟元素E | 3 |
E:first-of-type | 匹配同型別中的第一個同級兄弟元素E | 3 |
E:last-of-type | 匹配同型別中的最後一個同級兄弟元素E | 3 |
E:only-child | 匹配父元素僅有的一個子元素E | 3 |
E:only-of-type | 匹配同型別中的唯一的一個同級兄弟元素E | 3 |
E:empty | 匹配沒有任何子元素(包括text節點)的元素E | 3 |
E:not(s) | 匹配不含有s選擇符的元素 | 3 |
E:target | 匹配文件中特定”id”點選後的效果 | 3 |
偽元素選擇器
選擇器 | 描述 | 版本 |
---|---|---|
E::first-letter | 選擇文字塊的第一個字母 | 3 |
E::first-line | 選擇元素的第一行 | 3 |
E::before | 在元素前面插入內容,配合”content”使用 | 3 |
E::after | 在元素後面插入內容,配合”content”使用 | 3 |
E::selection | 設定物件被選擇時的樣式 | 3 |
讓IE6-8支援偽類和屬性選擇器
如何才能讓IE6~8支援CSS3偽類和屬性選擇器,也許你已經想到了,我們會用JavaScript工具來進行輔助,那麼剛好|8e50989464f7517425e2c31ba2d6dd59424|就可以完成這件事情,而且使用起來很簡單,只要把selectivizr.js
引入到頁面上就可以了,如下:
1 2 3 4 5 |
<!- -[if (gte IE 6)&(lte IE 8)]> <script type="text/javascript" src="selectivizr.js"></script> <![endif]- -> |
但是使用它還有一些注意事項:
- 必須要引用一個JavaScript庫,比如jQuery
- 只能解析
<link>
標籤引入的樣式,如果是<style>
定義的樣式是不會解析的 - 動態生成的DOM不會做二次對映
- 需要在標準模式的DTD才能夠生效
常見問題與Bug
*
萬用字元造成繼承失效
1 2 3 4 5 6 7 |
* { color:red; } #test{ color:blue; } |
1 2 3 |
<div id='test'> <a href="#">text</a> </div> |
▲ 最終text的顏色卻是紅色的
按照我們的理解, id 的優先順序是高於 * 萬用字元的,而文字也本應該繼承 id 元素的color值,所以最終的文字應該是藍色呀。
所以這裡混淆了一個概念,繼承的樣式的優先順序永遠低於元素本身的樣式,包括萬用字元選擇器,所以大家在開發中,應該儘可能的避免濫用萬用字元,以免帶來一些隱性問題。
關於這個問題,還可以參考《關於CSS特殊性的問題》
而在IE6及更早瀏覽器並不支援通配選擇符(*
),只是將它忽略了,所以也變相的能看到效果。
E:hover 失效
E:hover 偽類用於設定元素在其滑鼠懸停時的樣式,但是在某種情況會導致效果失效,如下:
1 2 3 4 5 6 7 8 9 10 |
#test { background:red; } #test div { display:none; } #test:hover div{ display:block; background:yellow; } |
1 |
<div id="test">觸發我<div>看到我了吧</div></div> |
▲ 當觸發
#test:hover 時,此效果是在IE6中是無效的,因為在IE6中,
E:hover 偽類僅能用於a(超連結)物件,且該a
物件必須要擁有href
屬性。
E:hover 還有一種失效的狀態,是大家最常見的,程式碼如下:
1 2 3 4 |
a:link {color:gray;} a:hover{color:green;} a:visited{color:yellow;} a:active{color:blue;} |
1 |
<a href="#nogo">文字</a> |
▲ 當超連結處於 a:hover 時,你會發現其效果是無效,文字的顏色不會變成綠色,這是因為超連結的偽類樣式書寫是有固定順序的,不能顛倒,這四個屬性正確的定義順序為:
1 2 3 4 |
a:link {} a:visited {} a:hover {} a:active {} |
E:focus 失效
1 2 3 |
#test:focus + p { font-weight:bold; } |
1 2 |
<button id="test">點選我觸發focus</button> <p>文字</p> |
▲ 當點選 button 按鈕觸發 :focus 時將鄰近元素的文字進行加粗,但是這個效果在IE8是失效的,如何來修復它呢,只需要新增一個空的 :focus 選擇器,如下:
1 2 3 4 |
#test:focus + p { font-weight:bold; } #test:focus {} |
E:first-line 失效
如果在當前選擇器內使用了 !important , :first-line 偽類內部的定義的屬性會被完全忽略,示例:
1 2 3 4 5 6 |
p { color:blue; } p:first-line { color:red !important; } |
1 |
<p>第一行文字,<br/>第二行文字</p> |
▲ 正常情況下第一行的文字會變成紅色,但是在IE8瀏覽器卻忽略它沒有任何變化,如何來解決這個問題呢,把 !important 去掉就好了,如下:
1 2 3 4 5 6 |
p { color:blue; } p:first-line { color:red; } |
E:first-letter 失效
E:first-letter 失效和 E:first-line 失效的問題是相同的,解決方案請參考上方。
E > F 失效
就是子選擇器中間有註釋會導致屬性失效,如下:
1 2 3 4 5 |
#test > /*子選擇器*/ p { color:red; } |
1 2 3 |
<div id="test"> <p>文字</p> </div> |
▲ 如果你非要這樣寫註釋,那麼在IE7下會導致子選擇器失效,同樣, E + F 鄰近選擇器也有同樣的問題,如何解決呢,不在選擇器中間新增註釋就可以了。
效能優化
CSS 選擇器我們都在使用,但是如何讓它變的更簡潔,高效呢?
首先選擇器對效能的影響源於瀏覽器匹配選擇器和文件元素時所消耗的時間,所以優化選擇器的原則是應儘量避免使用消耗更多匹配時間的選擇器,但是在此之前我們需要先了解瀏覽器的匹配機制,就是它是如何讀取我們的選擇器的。
選擇器匹配機制
1 2 3 |
#nav > a { color:red; } |
當我們看到這個選擇器的時候,會認為首先會找到id為 nav 的元素,然後在找到其子元素,將樣式屬性應用到 a 元素上。
事實上,卻恰恰相反,因為瀏覽器讀取選擇器時,不是按照我們的閱讀習慣從左到右,而是遵循的從選擇器的右邊到左邊進行讀取的。
當我們知道這個匹配機制後,再回來看這個選擇器,瀏覽器必須先遍歷頁面中所有的
a 元素,然後查詢其父元素的id
是否為
nav ,這樣一來你就會發看似高效的選擇器在實際中的匹配開銷是很高的。
理解了CSS選擇器從右到左匹配的機制後,我們再看以下兩種選擇器:
1 |
div #nav |
1 |
#nav div |
你是否會認為第2種選擇器的效率要高於第1種,那麼就錯了,其實第一個選擇器的效率更高,因為第一個選擇器的關鍵選擇器使用了 #id 選擇器”,而第二個選擇器的關鍵選擇器使用的是 div 標籤選擇器。
這裡所說的關鍵選擇器,就是CSS選擇器中最右邊部分,它是被瀏覽器最先尋找的,那麼哪類選擇器是最高效的?哪個是會影響選擇器效率的關鍵選擇器?
選擇器效率
在上面內容中我們瞭解瀏覽器的匹配機制,以及關鍵選擇器的重要性,那麼哪些CSS選擇器能夠減少效能損耗呢?
Google 資深web開發工程師 Steve Souders 對 CSS 選擇器的執行效率從高到低做了一個排序:
- id選擇器(#id)
- 類選擇器(.className)
- 標籤選擇器(div,h1,p)
- 相鄰選擇器(h1+p)
- 子選擇器(ul > li)
- 後代選擇器(li a)
- 萬用字元選擇器(*)
- 屬性選擇器(a[rel=”external”])
- 偽類選擇器(a:hover,li:nth-child)
從Steve Souders的CSS Test
我們可以看出
#id 選擇器和
.className 類選擇器在速度上的差異很小很小。而在一個a
標籤選擇器的測試上顯示,它比#id
選擇器和類選擇器的速度慢了很多,從這裡我們可以看出
#id 、
.className 選擇器 和
a 標籤、
li a 後代選擇器中間的差異較大,但是相互之間的差異較小。
接下來舉幾個示例:
1 2 |
#nav {} .menu{} |
1 2 |
p#nav {} p.menu {} |
▲ 上面的選擇器效率要高於下面的選擇器,標籤元素會降低選擇器效率
優化建議
我們理解了CSS選擇器從右到左匹配的機制,也瞭解關鍵選擇器的重要性,以及CSS選擇器的效率排序,那麼在使用選擇器的時候,通過避免不恰當的使用,來提升 CSS 選擇器效能。
避免使用通用選擇器
1 |
#nav * {…} |
▲ 這個選擇器所做的是選擇所有在頁面上的單個元素(是每個單個的元素),然後去看看它們是否有一個#nav
的父元素。這是非常不高效選擇器,開銷太大了,應該避免關鍵選擇器是通配選擇器的情況。
避免使用標籤或 class 選擇器限制 id 選擇器
1 2 3 4 5 6 7 |
/* Bad */ div#nav {…} .menuBalck#menu {…} /* Good */ #nav {…} #menu {…} |
▲ ID選擇器本身就是唯一的,加上div反而增加不必要的匹配;
避免使用標籤限制 class 選擇器
1 2 3 4 5 |
/* Bad */ span.red {…} /* Good */ .text-red {…} |
▲ 在標籤上定義 class 選擇器,在開發和維護時容易混淆,一般不建議這樣寫。
避免使用多層標籤選擇器。使用 class 選擇器替換,減少css查詢
1 2 3 4 5 |
/* Bad */ a[href="#"] > span > em {…} /* Good */ .className {} |
▲ 這種情況建議直接定義.className 選擇器,然後使用 <em class="className"></em>
避免過渡使用子選擇器
1 2 3 4 5 6 |
/* Bad */ div ul li a {} div > ul > li > a {} /* Good */ .className {…} |
▲ 這種情況建議直接定義.className 選擇器,然後使用 <a class="className"></a>
避免過度限制選擇器
1 2 3 4 5 |
/* Bad */ html body .wrapper #content a {} /* Good */ #content a {} |
▲ 這裡至少有3個選擇器是完全不需要的,過度限制選擇器使瀏覽器工作比它實際需要的更繁重,花費的時間更多,所以這裡應該避免。
利用可繼承性
1 2 3 4 5 |
/* Bad */ #nav > li > a { color:red; } /* Good */ #nav { color:red; } |
▲ 在使用選擇器之前,請先考慮利用繼承性實現
下一節繼續整理選擇器的優先順序和繼承性相關內容。