玩轉CSS選擇器(二) 之 瀏覽器支持,常見Bug,性能優化

前言

上一篇系列文章整理了CSS選擇器的基礎使用方法,由於內容較多且細緻,寫了不少DEMO,目前將它整理成適合移動端瀏覽器的CSS選擇器的參考手冊,方便學習CSS的人蔘考使用,立刻就要搞定了,以後會放出 (笑臉)。javascript

本節內容會跟着上一節的內容繼續完善,首先會補充CSS選擇器的瀏覽器支持狀況(主要是說IE),好比咱們最經常使用的s1,s2,…,sN羣組選擇器在IE7時才被支持,而且IE7還支持了不少咱們沒有想到的選擇器,如子元素選擇器,屬性選擇器,瞭解後你會發現IE7仍是挺了不得的。css

以後還會補充日常使用選擇器遇到的一些問題以及解決方案,最後瞭解瀏覽器是如何讀取選擇器的,怎樣使用選擇器能達到高效率。html

瀏覽器支持

了不得的IE7

當咱們在開發網頁時,若是網頁須要兼容IE6,那麼天然地會把IE6和IE7瀏覽器歸爲一路貨色,對於不兼容的選擇器和屬性都將再也不考慮使用,但是你是否知道IE7相比IE6增長了許多選擇器能夠用,如羣組選擇器,相鄰選擇器,兄弟選擇器,屬性選擇器。java

如下選擇器是不支持IE6,僅支持 IE7 及以上的瀏覽器web

基本選擇器

選擇器 描述 版本
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在屬性選擇器中是不被支持的,這部份內容會在下面的常見問題中詳細說明。segmentfault

僞類選擇器

選擇器 描述 版本
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:beforeE:after配合content屬性都在IE8中獲得了很好的支持。性能優化

如下選擇器不支持IE6,IE7,僅支持 IE8 及以上的瀏覽器app

僞類選擇器

選擇器 描述 版本
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引入到頁面上就能夠了,以下:

<!- -[if (gte IE 6)&(lte IE 8)]>

      <script type="text/javascript" src="selectivizr.js"></script>

<![endif]- ->

可是使用它還有一些注意事項:

  1. 必需要引用一個JavaScript庫,好比jQuery

  2. 只能解析<link>標籤引入的樣式,若是是<style>定義的樣式是不會解析的

  3. 動態生成的DOM不會作二次映射

  4. 須要在標準模式的DTD纔可以生效

項目地址:http://selectivizr.com

常見問題與Bug

* 通配符形成繼承失效

* {
    color:red;
}

#test{
    color:blue; 
}
<div id='test'>
    <a href="#">text</a>
</div>

▲ 最終text的顏色倒是紅色的

按照咱們的理解,id的優先級是高於*通配符的,而文字也本應該繼承id元素的color值,因此最終的文字應該是藍色呀。

因此這裏混淆了一個概念,繼承的樣式的優先級永遠低於元素自己的樣式,包括通配符選擇器,因此你們在開發中,應該儘量的避免濫用通配符,以避免帶來一些隱性問題。

關於這個問題,還能夠參考《關於CSS特殊性的問題

而在IE6及更早瀏覽器並不支持通配選擇符(*),只是將它忽略了,因此也變相的能看到效果。

E:hover 失效

E:hover僞類用於設置元素在其鼠標懸停時的樣式,可是在某種狀況會致使效果失效,以下:

#test {
    background:red;
}
#test div { 
    display:none;
}
#test:hover div{
    display:block;
    background:yellow;
}
<div id="test">觸發我<div>看到我了吧</div></div>

▲ 當觸發#test:hover時,此效果是在IE6中是無效的,由於在IE6中,E:hover僞類僅能用於a(超連接)對象,且該a對象必需要擁有href屬性。

E:hover還有一種失效的狀態,是你們最多見的,代碼以下:

a:link {color:gray;}
a:hover{color:green;}
a:visited{color:yellow;}
a:active{color:blue;}
<a href="#nogo">文字</a>

▲ 當超連接處於a:hover時,你會發現其效果是無效,文字的顏色不會變成綠色,這是由於超連接的僞類樣式書寫是有固定順序的,不能顛倒,這四個屬性正確的定義順序爲:

a:link {}
a:visited {}
a:hover {}
a:active {}

E:focus 失效

#test:focus + p {
    font-weight:bold;
}
<button id="test">點擊我觸發focus</button>
<p>文字</p>

▲ 當點擊button按鈕觸發:focus時將鄰近元素的文字進行加粗,可是這個效果在IE8是失效的,如何來修復它呢,只須要添加一個空的:focus選擇器,以下:

#test:focus + p {
    font-weight:bold;
}
#test:focus {}

E:first-line 失效

若是在當前選擇器內使用了!important:first-line僞類內部的定義的屬性會被徹底忽略,示例:

p {
    color:blue;
}
p:first-line {
    color:red !important;
}
<p>第一行文字,<br/>第二行文字</p>

▲ 正常狀況下第一行的文字會變成紅色,可是在IE8瀏覽器卻忽略它沒有任何變化,如何來解決這個問題呢,把!important去掉就行了,以下:。

p {
    color:blue;
}
p:first-line {
    color:red;
}

E:first-letter 失效

E:first-letter失效和E:first-line失效的問題是相同的,解決方案請參考上方。

E > F 失效

就是子選擇器中間有註釋會致使屬性失效,以下:

#test > 
/*子選擇器*/
p {
    color:red;
}
<div id="test">
    <p>文字</p>
</div>

▲ 若是你非要這樣寫註釋,那麼在IE7下會致使子選擇器失效,一樣,E + F鄰近選擇器也有一樣的問題,如何解決呢,不在選擇器中間添加註釋就能夠了。

性能優化

CSS 選擇器咱們都在使用,可是如何讓它變的更簡潔,高效呢?
首先選擇器對性能的影響源於瀏覽器匹配選擇器和文檔元素時所消耗的時間,因此優化選擇器的原則是應儘可能避免使用消耗更多匹配時間的選擇器,可是在此以前咱們須要先了解瀏覽器的匹配機制,就是它是如何讀取咱們的選擇器的。

選擇器匹配機制

#nav > a {
    color:red;
}

當咱們看到這個選擇器的時候,會認爲首先會找到id爲nav的元素,而後在找到其子元素,將樣式屬性應用到a元素上。

事實上,卻偏偏相反,由於瀏覽器讀取選擇器時,不是按照咱們的閱讀習慣從左到右,而是遵循的從選擇器的右邊到左邊進行讀取的

當咱們知道這個匹配機制後,再回來看這個選擇器,瀏覽器必須先遍歷頁面中全部的 a 元素,而後查找其父元素的id是否爲nav,這樣一來你就會發看似高效的選擇器在實際中的匹配開銷是很高的。

理解了CSS選擇器從右到左匹配的機制後,咱們再看如下兩種選擇器:

div #nav
#nav div

你是否會認爲第2種選擇器的效率要高於第1種,那麼就錯了,其實第一個選擇器的效率更高,由於第一個選擇器的關鍵選擇器使用了#id選擇器」,而第二個選擇器的關鍵選擇器使用的是div標籤選擇器。

這裏所說的關鍵選擇器,就是CSS選擇器中最右邊部分,它是被瀏覽器最早尋找的,那麼哪類選擇器是最高效的?哪一個是會影響選擇器效率的關鍵選擇器?

選擇器效率

在上面內容中咱們瞭解瀏覽器的匹配機制,以及關鍵選擇器的重要性,那麼哪些CSS選擇器可以減小性能損耗呢?

Google 資深web開發工程師 Steve Souders 對 CSS 選擇器的執行效率從高到低作了一個排序:

  1. id選擇器(#id)

  2. 類選擇器(.className)

  3. 標籤選擇器(div,h1,p)

  4. 相鄰選擇器(h1+p)

  5. 子選擇器(ul > li)

  6. 後代選擇器(li a)

  7. 通配符選擇器(*)

  8. 屬性選擇器(a[rel="external"])

  9. 僞類選擇器(a:hover,li:nth-child)

從Steve Souders的CSS Test咱們能夠看出#id選擇器和.className類選擇器在速度上的差別很小很小。而在一個a標籤選擇器的測試上顯示,它比#id選擇器和類選擇器的速度慢了不少,從這裏咱們能夠看出#id.className選擇器 和 a標籤、li a後代選擇器中間的差別較大,可是相互之間的差別較小。

接下來舉幾個示例:

#nav {}
.menu{}
p#nav {}
p.menu {}

▲ 上面的選擇器效率要高於下面的選擇器,標籤元素會下降選擇器效率

優化建議

咱們理解了CSS選擇器從右到左匹配的機制,也瞭解關鍵選擇器的重要性,以及CSS選擇器的效率排序,那麼在使用選擇器的時候,經過避免不恰當的使用,來提高 CSS 選擇器性能。

避免使用通用選擇器

#nav * {…}

▲ 這個選擇器所作的是選擇全部在頁面上的單個元素(是每一個單個的元素),而後去看看它們是否有一個#nav的父元素。這是很是不高效選擇器,開銷太大了,應該避免關鍵選擇器是通配選擇器的狀況。

避免使用標籤或 class 選擇器限制 id 選擇器

/* Bad */
div#nav {…}
.menuBalck#menu {…}

/* Good */
#nav {…}
#menu {…}

▲ ID選擇器自己就是惟一的,加上div反而增長沒必要要的匹配;

避免使用標籤限制 class 選擇器

/* Bad */
span.red {…}

/* Good */
.text-red {…}

▲ 在標籤上定義class選擇器,在開發和維護時容易混淆,通常不建議這樣寫。

避免使用多層標籤選擇器。使用 class 選擇器替換,減小css查找

/* Bad */
a[href="#"] > span > em {…}

/* Good */
.className {}

▲ 這種狀況建議直接定義.className 選擇器,而後使用<em class="className"></em>

避免過渡使用子選擇器

/* Bad */
div ul li a {}
div > ul > li > a {}

/* Good */
.className {…}

▲ 這種狀況建議直接定義.className 選擇器,而後使用<a class="className"></a>

避免過分限制選擇器

/* Bad */
html body .wrapper #content a {}

/* Good */
#content a {}

▲ 這裏至少有3個選擇器是徹底不須要的,過分限制選擇器使瀏覽器工做比它實際須要的更繁重,花費的時間更多,因此這裏應該避免。

利用可繼承性

/* Bad */
#nav > li > a { color:red; } 

/* Good */
#nav { color:red; }

▲ 在使用選擇器以前,請先考慮利用繼承性實現

下一節繼續整理選擇器的優先級和繼承性相關內容。

相關文章
相關標籤/搜索