深入解析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;    }  }