如何撰寫有效率的CSS選擇器(CSS Selector)


作者: | 2011/10/11 | 5 則迴響


高效率的CSS選擇器(Selector)表示法不是一個新的主題,對於網頁設計師而言,知道如何撰寫CSS,是一個基本的技術能力,而了解如何撰寫高效率的CSS則能讓網頁設計師的CSS作品獲得更好的品質。

注意:這裡所指的不是讓開發者很有效率的寫CSS,而是讓開發者寫出來的CSS Selector能很有效率的被瀏覽器執行。

本篇描述的規則,比較適用於非常需要全面提升「速度」的網站,像是每個頁面的DOM元素都超過1000個以上的網站。如果是像我這種小小的部落格,提升的效率就完全不明顯,但身為專業的網頁設計師,你還是必須把這些規則放在心上。

CSS 選擇器(Selectors)

CSS 選擇器你應該不陌生,我們可以透過基本的選擇器去指定要處理的DOM元素,像是指定標籤(div,span,body…)、ID(#header)、Class(.post)等選擇器。

還有一些較不常見的摸擬類別(pseudo-classes),如 :hover,或更為複雜的CSS3及regex選擇器,如 :first-child或 [class^=”grid-“] 表示挑出所有類別名稱以grid-為開頭的元素。

而根據網站效率專家 Steve Souders 指出,各種CSS選擇器的效率由高至低排序如下:

1. ID (#id)
2. Class (.class)
3. Type (即HTML標籤,如div)
4. 鄰接選擇器 (如: h2+p,僅作用於鄰接h2的p元素)
5. Child (如: li>ul)
6. Descendant (如:ul a)
7. Universal (*)
8. 屬性 (如: [type=”text”])
9. 摸擬類別/元素 (如: a:hover)

值得注意的是,雖然ID在技術性上來說比較快,但差異其實很微小。在Windows上的Firefox6上測試,ID的reflow速度還比Class慢。但兩者間reflow速度的差距根本不值一提。

註1:reflow是指css在為網頁加上樣式的過程之一。整個CSS繪製過程會先建立DOM,再進行reflow來確定各元素的位置,最後再進行繪製(render)樣式的動作。這裡有個reflow Mozilla官網的過程影片:

如果影片失效,請點選這裡,可以看見reflow奇妙的工作方式。

註2:測試方式是用Steve Sounders製作的工具:css-selectors

接合選擇器

你還可以用像是#nav a這種接合(combining)多個選擇器的選擇器,它的意思是「找到所有在ID為nav的元素下的a元素」。我們以自然語言的閱讀方式,來閱讀這種接合選擇器的方式,通常就是這樣用「由左至右」的方式來讀,但是瀏覽器不是這樣讀的,瀏覽器是用「由右至左」的方式來理解選擇器規則的。

這是為了效率而設計的讀取方式,理由可參考這個討論串。於是,瀏覽器就從DOM樹的上至下開始它的剖析查找旅程。

關鍵選擇器 (key selector)

關鍵選擇器就是最靠接合選擇器右邊的選擇器,例如:#nav a,關鍵選擇器就是a,它就是瀏覽器第一個尋找的規則。是的,瀏覽器會先找出所有的a元素,還記得上面我們剛討論過的效率排行嗎?此時就是該選擇器效率表現的時刻。找出所有的a元素後,接著它會回頭去看DOM樹,去找看是否有a元素是住在ID為nav的元素之中。

因此,以下的選擇器查找規則,就不是非常有效率:
[css]
#content *{}
[/css]

這樣的選擇器會先找出「所有」頁面上的元素,接著再找是否有任何元素是位於#content底下的。如此的查找成本非常昂貴。

所以,運用這樣的知識,我們可以為訂定CSS樣式做出更好的決策。想像一下,如果你有一個充滿大量資料的網頁,而你是一個超級大站。在如此頁面裡,有成千上百個a元素。裡頭有一小塊區域的連結,是專門留給社群連結的區塊,它們都放在ID為social的ul元素裡,假設裡面有Twitter、Facebook、Google+等連結。因此,在這個頁面裡,我們共有三個社群連結,和其他成千上百個連結。

在這樣的頁面裡,使用以下這個選擇器,毫無疑問是非常沒有效率的表示法:
[css]
#social a{}
[/css]

如果要改善這個表示法,我們可以為#social裡頭的a,加上特定的class,例如:.social-link。但只標上 .social-link 似乎有點違背精實運用CSS類別的好習慣,因為這看來沒什麼意義,要達成折衷用法,我們可以這樣寫:
[html]
<!– 略 –>
<ul id="social">
<li><a href="#" class="social-link twitter">Twitter</a></li>
<li><a href="#" class="social-link facebook">Facebook</a></li>
<li><a href="#" class="social-link gplus">Google+</a></li>
</ul>
<!– 略 –>

[/html]

因此,我們的選擇器就可以改成:
[css]
#social .social-link{}
[/css]

這個新的選擇器將比對「遠低於」先前所需比對的元素,這表示瀏覽器就可以快速的找到目標元素,並且快速的完成樣式的繪製。

過份限制的選擇器

接著我們進一步來看其他的最佳化建議。這是一個「過份限制」的選擇器例子:
[css]
html body .wrapper #content a{}
[/css]

這是一個典型限定過了頭的選擇器例子,其中至少有三個元素是多餘的,拿掉後剩下:
[css]
#content a{}
[/css]

這表示,在瀏覽器查找到#content後,不用再進一步去找.wrapper、body和html,因為#content是唯一的,找到#content後,其後的查找都是多餘的。

再看一個例子:
[css]
ul#nav li a{}
[/css]

就可改成:
[css]
#nav a{}
[/css]

知道這樣的規則後,你就不會再輕易的寫出像下面這種看來很炫,但卻沒啥效率的表示式了:
[css]
div:nth-of-type(3) ul:last-child li:nth-of-type(odd) *{ font-weight:bold }
[/css]

另外,jQuery所支援的CSS Selector,讀取方式也是從右至左的。

參考文章:http://csswizardry.com/2011/09/writing-efficient-css-selectors/


標籤:

分類:,

本文作者是Audi Lu

5 則留言

  • R+ says:

    關於【過份限制的選擇器】
    想請教一下
    若使用SPSS或是Compass/Compass.app之類 加速CSS開發的輔助程式
    該如何避免這種過份限制的問題呢?

    (
    因為層疊式的SPSS規則似乎較會出現 其實不必出現的親代選擇器
    但是spss這類程式寫出來的東西卻似乎又比較好維護XD
    )

    感謝您~

  • Canap says:

    但是還是要讓開發者可以很有效率的寫CSS,才能讓CSS Selector很有效率的被瀏覽器執行,噗

    • mrmu says:

      您好,我同意讓開發者很有效率的寫CSS也很重要,這可能是類似像compass/sass, LESS這類css preprocessor工具的目標,是另一個有趣的主題,我也還在學習中。:)

  • G says:

    很棒的文章,改進了不少 CSS 觀念,感謝分享 🙂

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*

*

*

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料