談一談CSS的選擇器和工作原理
既然我們是談CSS,那我們先來說一下什麼是CSS?
層疊樣式表(英文全稱:Cascading Style Sheets)是一種用來表現HTML(標準通用標記語言的一個應用)或XML(標準通用標記語言的一個子集)等檔案樣式的計算機語言。CSS不僅可以靜態地修飾網頁,還可以配合各種指令碼語言動態地對網頁各元素進行格式化。CSS 能夠對網頁中元素位置的排版進行畫素級精確控制,支援幾乎所有的字型字號樣式,擁有對網頁物件和模型樣式編輯的能力。
之所以叫“層疊”,就是因為在 CSS 中,一個檔案的樣式可以從其他的樣式表中繼承下來。讀者在有些地方可以使用他自己更喜歡的樣式,在其他地方則繼承,或“層疊”作者的樣式,這種層疊的方式使作者和讀者都可以靈活地加入自己的設計,混合各人的愛好。哈哈,是不是覺得設計得很美妙……O(∩_∩)O哈哈~
做個web前端開發的人都知道,css提供了大量的選擇器。在 CSS 中,選擇器是一種模式,用於選擇需要新增樣式的元素。
"CSS" 列指示該屬性是在哪個 CSS 版本中定義的。
選擇器 | 例子 | 例子描述 | CSS |
---|---|---|---|
.intro | 選擇 class="intro" 的所有元素。 | 1 | |
#id | #firstname | 選擇 id="firstname" 的所有元素。 | 1 |
* | * | 選擇所有元素。 | 2 |
p | 選擇所有 <p> 元素。 | 1 | |
div,p | 選擇所有 <div> 元素和所有 <p> 元素。 | 1 | |
div p | 選擇 <div> 元素內部的所有 <p> 元素。 | 1 | |
div>p | 選擇父元素為 <div> 元素的所有 <p> 元素。 | 2 | |
div+p | 選擇緊接在 <div> 元素之後的所有 <p> 元素。 | 2 | |
[target] | 選擇帶有 target 屬性所有元素。 | 2 | |
[target=_blank] | 選擇 target="_blank" 的所有元素。 | 2 | |
[title~=flower] | 選擇 title 屬性包含單詞 "flower" 的所有元素。 | 2 | |
[lang|=en] | 選擇 lang 屬性值以 "en" 開頭的所有元素。 | 2 | |
:link | a:link | 選擇所有未被訪問的連結。 | 1 |
a:visited | 選擇所有已被訪問的連結。 | 1 | |
a:active | 選擇活動連結。 | 1 | |
a:hover | 選擇滑鼠指標位於其上的連結。 | 1 | |
input:focus | 選擇獲得焦點的 input 元素。 | 2 | |
p:first-letter | 選擇每個 <p> 元素的首字母。 | 1 | |
p:first-line | 選擇每個 <p> 元素的首行。 | 1 | |
p:first-child | 選擇屬於父元素的第一個子元素的每個 <p> 元素。 | 2 | |
p:before | 在每個 <p> 元素的內容之前插入內容。 | 2 | |
p:after | 在每個 <p> 元素的內容之後插入內容。 | 2 | |
p:lang(it) | 選擇帶有以 "it" 開頭的 lang 屬性值的每個 <p> 元素。 | 2 | |
a[src^="https"] | 選擇其 src 屬性值以 "https" 開頭的每個 <a> 元素。 | 3 | |
a[src$=".pdf"] | 選擇其 src 屬性以 ".pdf" 結尾的所有 <a> 元素。 | 3 | |
a[src*="abc"] | 選擇其 src 屬性中包含 "abc" 子串的每個 <a> 元素。 | 3 | |
p:first-of-type | 選擇屬於其父元素的首個 <p> 元素的每個 <p> 元素。 | 3 | |
p:last-of-type | 選擇屬於其父元素的最後 <p> 元素的每個 <p> 元素。 | 3 | |
p:only-of-type | 選擇屬於其父元素唯一的 <p> 元素的每個 <p> 元素。 | 3 | |
p:only-child | 選擇屬於其父元素的唯一子元素的每個 <p> 元素。 | 3 | |
p:nth-child(2) | 選擇屬於其父元素的第二個子元素的每個 <p> 元素。 | 3 | |
p:nth-last-child(2) | 同上,從最後一個子元素開始計數。 | 3 | |
p:nth-of-type(2) | 選擇屬於其父元素第二個 <p> 元素的每個 <p> 元素。 | 3 | |
p:nth-last-of-type(2) | 同上,但是從最後一個子元素開始計數。 | 3 | |
p:last-child | 選擇屬於其父元素最後一個子元素每個 <p> 元素。 | 3 | |
:root | :root | 選擇文件的根元素。 | 3 |
p:empty | 選擇沒有子元素的每個 <p> 元素(包括文字節點)。 | 3 | |
#news:target | 選擇當前活動的 #news 元素。 | 3 | |
input:enabled | 選擇每個啟用的 <input> 元素。 | 3 | |
input:disabled | 選擇每個禁用的 <input> 元素 | 3 | |
input:checked | 選擇每個被選中的 <input> 元素。 | 3 | |
:not(p) | 選擇非 <p> 元素的每個元素。 | 3 | |
::selection | 選擇被使用者選取的元素部分。 | 3 |
但是到現在為止,有時候已有的選擇器還有不夠完美的地方,比如還不存在下面的幾款
child < parent 即根據已知的子節點找到他的直接父節點,跟
> 選擇直接孩子結點對應
child
$ ancestors 即選擇孩子結點的所以祖先結點們
A - B 即選擇A節點前面的兄弟節點B,類似於jquery裡的A.prev(),與現有的 + 選擇後面的兄弟結點相對應
但是查閱W3C相關資料得知,CSS一直幾乎沒有這樣的選擇器(現在是沒有,希望以後有啊……哈哈^_^),是因為這樣的選擇器會導致回溯。
那問題來了,什麼是回溯呢?
那還得從瀏覽器是如何載入css講起。瀏覽器最初設計的時候就考慮了漸進顯示,也就是整個文件載入了多少就顯示多少內容,而不用等整個下載完。瀏覽器下載的順序是從上到下,渲染的順序也是從上到下,下載和渲染是同時進行的。在渲染到頁面的某一部分時,其上面的所有部分都已經下載完成(並不是說所有相關聯的元素都已經下載完)。如果遇到語義解釋性的標籤嵌入檔案(JS指令碼,CSS樣式),那麼此時IE的下載過程會啟用單獨連線進行下載。並且在下載後進行解析,解析過程中,停止頁面所有往下元素的下載。 樣式表在下載完成後,將和以前下載的所有樣式表一起進行解析,解析完成後,將對此前所有元素(含以前已經渲染的)重新進行渲染。漸進顯示在CSS上的原理就是一個節點所適用的樣式只取決於它和它之前的節點(父節點、它之前的兄弟節點)的性質。而我們剛才提到的幾款選擇器則恰好相反,跟這個是背道而行的。言外之意,就是說當瀏覽器解析到一個新節點時,可能改變之前節點所適用的樣式——因而要求在解析一個新節點後,得回頭重新計算之前節點所匹配的樣式,此即所謂“回溯”(就像我們現實中的水再往回流了,O(∩_∩)O哈哈~)。在最壞的情況下所導致大量的重新計算和reflow,可以相當於重新載入整個網頁,在某種程度上來說,這是挺耗資源的。
現在我們來看下CSS是怎麼工作的?
CSS
語言是一種標記語言,它不需要編譯,可以直接由瀏覽器解釋執行。瀏覽器CSS匹配不是從左到右進行查詢,而是從右到左進行查詢。比如 div#box p span.red{color:red;},瀏覽器的查詢順序如下:先查詢 html 中所有 class=’red’ 的 span 元素,找到後,再查詢其父輩元素中是否有p元素,再判斷p的父元素中是否有 id 為 box 的 div 元素,如果都存在,則 CSS 匹配上。
瀏覽器從右到左進行查詢的好處是為了儘早過濾掉一些無關的樣式規則和元素。Firefox稱這種查詢方式為 keyselector(關鍵字查詢),所謂的關鍵字就是樣式規則中最後(最右邊)的規則,上面的 key 就是 span.red。
最後談一下常見的幾種CSS優化:
1、不要在id選擇器前使用標籤名
一般寫法:div#box
更好寫法:#box
解釋:因為id選擇器是唯一的,加上div反而增加不必要的 CSS 匹配,這樣就會顯得多此一舉了。
2、不要在 class 選擇器前使用標籤名
一般寫法:span.red
更好寫法:.red
解釋:同上,但如果你定義了多個.red,而且在不同的元素下是樣式不一樣,則不能去掉,比如你css檔案中定義如下:
p.red{color:red;}
span.red{color:#ff00ff}
如果是這樣定義的就不要去掉,去掉後就會混淆,不過建議最好不要這樣寫
3、儘量少使用層級關係
一般寫法:#box.red{color:red;}
更好寫法:.red{color:red;}
4、使用 class 代替層級關係
一般寫法:#box ul li a{display:block;}
更好寫法:.block{display:block;}
5、在 CSS 渲染效率中 id 和 class 的效率是基本相當的
class 會在第一次載入中被快取,在層疊中會有更加好的效果,在根部元素採用id會具有更加好(id有微妙的速度優勢)。