深入解析CSS樣式優先順序
- 2019 年 11 月 28 日
- 筆記
作為前端多多少少都會對CSS樣式的權重有一定的了解。最常用的方法就是對不同的選擇器分配不同的權重比,常見的就是
選擇器 |
權重值 |
---|---|
!important標識 |
10000 |
行內樣式 |
1000 |
id選擇器 |
100 |
類選擇器 |
10 |
標籤選擇器 |
1 |
通配符 * |
0 |
具體的判斷我們可以用一個矩陣來表示:(0, 0, 0, 0, 0)。那這裡面的每一個矩陣的0表示的是
- 第一個:
!important
的個數 - 第二個:行內的個數
- 第三個:id選擇器的個數
- 第四個:類選擇器的個數
- 第五個:標籤選擇器的個數
行內除了!important
高於其他的樣式。同時,這裡的這個矩陣實際上是不存在的,是認為的構建出來的。因為!important
始終最高,所以這裡可以忽略第一個,最後矩陣為(0, 0, 0, 0)。
上面表格中是我們在寫css樣式的時候需要了解最的基本的知識,也是最為普通的選擇器權重分配。權重值越高,權重越高,那麼樣式的優先順序就越高。所以,!important
的權重是最高的。正式因為這一點,所以!important
在編寫css樣式的時候一般都不建議使用,因為使用了!important
後,要想在修改樣式,幾乎是不可能的,即,樣式會變得不好控制。
其次是行內樣式。在現在前端開發中,為了更好的閱讀程式碼,是程式碼調理更清晰,一般都會把CSS程式碼以及JS程式碼從HTML中分離,採用外鏈的方式引入CSS以及JS。所以,行內樣式在一般的開發中,用的也不多。但是,在使用前端三大框架或者是需要使用JS來控制樣式的時候,多多少少的都會涉及到把樣式寫在HTML元素行內的情況。
接著是ID選擇器。在編寫樣式的時候,我們一般都不會使用ID選擇器來控制樣式,同時也不會在HTML文檔中添加過多的ID選擇器。ID選擇器一般更多的是用於獲取元素,而不是用來控制CSS樣式。
再者是類選擇器。這個在CSS樣式的編寫中用的算是最多的一種,因為一個標籤可以添加多個類名,不像ID只能添加一個,編寫不同的類名來控制不同的樣式顯示,同時根據權重來控制樣式的覆蓋。
然後是標籤選擇器,這個在開發中也是不建議使用,更多的是建議添加一個類名來控制,以實現復用,同時方便控制。
最後是通配符選擇器,這個選擇器的使用一般就是初始化文檔結構。例如
* { margin: 0; padding: 0; }
但是更多的公司採取的是具有針對的樣式重置,比如reset.css。
以上就是常用的樣式選擇器以及他們的權重。
主要選擇器的權重比較
權重累加
上面的權重值我們已經知道了,那麼具體怎麼來算呢?個人認為,需要記住一這點就OK了。相同類型的權重值累加,然後在比較相同類型選擇器的值
。舉個?
<div id="box" class="box div"></div>
div { width: 100px; height: 100px; } #div { background: green; } div.div { background: red; }
上面的程式碼中,來看看這個div盒子的顏色應該是什麼呢?分析一下CSS樣式:
根據上面的矩陣(0, 0, 0, 0)來分析。
#div
的矩陣為(0, 0, 1, 0, 0),最後的權重值為:1*100 = 100。
div.div
的矩陣為(0, 0, 0, 1, 1),最後的權重值為:1 10 + 1 1 = 11。
所以最後的樣式顯示 background: green;
有了這樣的結論,我這裡又做了一個測試。有下面的一段html程式碼與樣式
<div class="box"> <div class="box1"> <div class="box2"> <div class="box3"> <div class="box4"> <div class="box5"> <div class="box6"> <div class="box7"> <div class="box8"> <div class="box9"> <div id="box10" class="box10"></div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div>
div { width: 100px; height: 100px; } #box10 { background: green; } .box .box1 .box2 .box3 .box4 .box5 .box6 .box7 .box8 .box9 .box10 { background: red; }
按理說 #box10
的權重為 100 * 1 = 100
.box .box1 .box2 .box3 .box4 .box5 .box6 .box7 .box8 .box9 .box10
的權重為 10 * 11 = 110
下面使用多個類的權重值理論上是高於上面只使用一個id選擇器的情況,但是最後的結構卻不是我們想像的那樣。結果還是 green 。沒錯,後面的11個類的樣式無效。經過這樣的測試,我們可以猜想,在一個元素使用了ID選擇器修飾了樣式以後,如果在使用類選擇器,這時候是是沒有辦法使相同的樣式屬性生效,生效的依然是那個ID選擇器修飾的樣式。為什麼權重值大也沒有用呢?我猜或許是因為寫了太多類名的時候再和ID相比的話,瀏覽器會自己去判斷,選擇最優的那個,畢竟10多個類名在實際的開發中是不存在的。在張鑫旭大神的有趣:256個class選擇器可以幹掉1個id選擇器有一個實驗,但是這個實驗室在2012年的時候,那個時候,還是使用的IE瀏覽器能夠呈現出來。但是現在大部分的瀏覽器都不能夠呈現出256個class幹掉一個id的情況了。同時,也證明了,上面我們定義的矩陣(0, 0, 0, 0)其實並不嚴謹,id與class之前的差距我們這裡以10作為一個標準,但是實際上可能達到100,或者1000。即1000個class幹掉一個ID。但是由於現代編碼一般要求class的層級書寫的時候不要超過四層,所以,目前這個問題也就沒有什麼意義了。
因此,上面的矩陣也可以作為我們判斷的標準。
其他類型選擇器的權重比較
上面說了幾種常用的選擇器了,但是還有一些選擇器也是在開發中會出現,但是不是太常用的一些選擇器。那麼,有哪些呢?
w3c中樣式選擇器的權重優先順序的排序如下
important > 內嵌樣式 > ID > 類 | 偽類 | 屬性選擇 > 標籤 | 偽元素 > 偽對象 > 繼承 > 通配符 | 子選擇器 | 相鄰選擇器
- 偽類選擇器,如
:hover
- 屬性選擇器,如
[type="text"]
- 偽元素選擇器,如
::first-letter
- 子選擇器
>
,相鄰兄弟選擇器+
等等
偽類的優先順序(:)
首先來看看偽類選擇器的優先順序。
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> div { margin-top: 10px; height: 100px; width: 100px; } .box1 { background: lime; } :hover { background: pink; } .box2 { background: red; } </style> </head> <body> <div id="box" class="box1"></div> <div id="box" class="box2"></div> </body>
上面的程式碼在codePen中的具體效果,可以看到

當將滑鼠分別移動到兩個div盒子上面的時候,前面的綠色盒子的背景色會發生變化,而紅色盒子不會。但是,都是 :hover
的一盒偽類。所以判定,偽類的權重與類的權重是相同的。
屬性選擇器的優先順序
同樣是上面的程式碼,我們把樣式改為
div { margin-top: 10px; height: 100px; width: 100px; } .box1 { background: purple; } [class="box1"] { background: lime; } :hover { background: pink; } [class="box2"] { background: red; } .box2 { background: purple; }
唯一不同的就是樣式中添加了屬性選擇器[class="box1"]
與[class="box2"]
。同時順序發生了改變。
程式碼可看[https://codepen.io/Anthony-Wilson/pen/YzKWZpL]https://codepen.io/Anthony-Wilson/pen/YzKWZpL

。
所以,屬性選擇的權重 = 類的權重 = 偽類的權重。三者是相等的,都是(0, 0, 1, 0);
偽元素選擇器(::)
偽元素作為一種特殊的存在,我認為它不應該放在優先順序裡面同其他的選擇器相比。比如 ::before
和 ::after
這兩種偽元素都是在文檔中添加一個假的元素,並不能夠設置 id class 等屬性。所以這裡可以把它作為一個唯一的存在。那麼他的權重我們可以看為1。同時沒有和他比較的。
子選擇器>
,相鄰兄弟選擇器+
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .box1 span { color: yellow; } .box > span { color: blue; } .box2 span { color: red; } </style> </head> <body> <div class="box box1"><span>我</span>是一個盒子</div> <div class="box box2"><span>我</span>是一個盒子</div> </body> </html>
上面的例子在codePen中的運行結果

可以看到,第一個我是藍色,第二個我是紅色。結合上面的程式碼,可以看出來子元素選擇器和普通的空格其實沒有太多的區別,同理兄弟選擇器其實也是一樣的。遇到這種情況,直接比較 class 與 標籤的個數就可以了。即同基本的權重判斷是相同的。
結果總結
經過上面的推想測試,可以大致的得出一個優先順序的結論:
!important > ID > class = 屬性 = 偽類 > 標籤 > 通配符 > 繼承 > 瀏覽器自帶屬性
在使用選擇器的時候儘可能的選擇使用 class選擇器或者屬性選擇器(針對於input這一類)來對元素設置樣式。一個是使用class可以復用,第二個可以更好的控制元素樣式。同時,關於class命名的規範建議使用BEM命名規範。
補充:2019年08月22日
在網上看到這樣的一道題
問題:已知如下程式碼,如何修改才能讓圖片寬度為 300px ?注意下面程式碼不可修改。
<img src=」1.jpg」 style=」width:480px!important;」>
期初看到這段程式碼一下子就想到這都 !important
了還有辦法限制它的寬度?真的是被無知限制了想像力。
這裡提供幾種方法實現修改寬度:
1、使用max-width
雖然使用了 important
,但是也只是添加到了 width
屬性上面,並不影響 max-width 。所以這裡設置了 max-width 即可生效。
img { max-width: 300px; }
2、使用 transform:scale
img { transform: scaleX(0.625); }
3、使用 zoom
這個和CSS3的縮放是一樣的效果
img { zoom: 0.625: }
4、使用 js
這個和CSS3的縮放是一樣的效果
document.getElementsByTagName("img")[0].setAttribute("style","width:300px!important;")
5、使用彈性盒模型
這個和CSS3的縮放是一樣的效果
img { box-sizing: border-box; padding: 0 90px; }
6、使用animation
img { animation: width 0s forwards; } @keyframes width { from { width: 300px; } to { width: 300px; } }