CSS並不簡單–選擇器大家庭

descire發表於2019-02-28

梳理選擇器的種類,理解選擇器的優先順序,從而避免莫名其妙的樣式重疊問題。

一、選擇器的種類

1、基本選擇器

  基本選擇器包含:通配選擇器、元素選擇器、ID選擇器、類選擇器和群組選擇器。

  對於類選擇器的命名,`-`和`_`兩者多有,具體可以看這裡

  通配選擇器(*),它用來選擇所有元素,不過這個選擇器的效率比較低,所以慎重選擇,但是瞅了一眼Boostrap4.0是這樣設定box-sizing:

    html {
        box-sizing: border-box;
    }
    *, *::before, *::after {
        box-sizing: inherit;
    }
複製程式碼

  所以吧,也不是不能用,不過也就這一處,還是得少用。

  ID選擇器,它的唯一性是規範,但是應用於多個元素,樣式同樣生效,不推薦這樣做。

  群組選擇器,通過逗號隔開,可以有效的減少一些冗餘程式碼,例如上面的例子中:

    *, *::before, *::after {
        box-sizing: inherit;
    }
複製程式碼

2、屬性選擇器

  屬性選擇器主要針對於元素的屬性值進行篩選,包含這幾種操作符:=、|=、~=、*=、^=和$=:

  • [attr=val]:屬性值為val;
  • [attr|=val]:屬性值為val或者以val-開頭;
  • [attr~=val]:屬性值為val或者以多個空格分開含有val;
  • [attr*=val]:屬性值包含val;
  • [attr^=val]:屬性值以val開頭;
  • [attr$=val]:屬性值以val結尾。

  屬性選擇器的應用很多,例如Vue中的scoped css也正是利用屬性選擇器實現的。

3、層次選擇器

  層次選擇器包含後代選擇器(E F)、子選擇器(E > F)、相鄰兄弟選擇器(E + F)和通用選擇器(E ~ F)。

  後代選擇器與子選擇器的區別:相比較前者,子選擇器的搜尋範圍侷限於兒子節點。

  相鄰兄弟選擇器,選擇E後面相鄰的兄弟節點F。很遺憾不能選擇前面的。這個選擇器的應用也很多,比如我們設定列表項之間的距離為20px:

    li:not(:last-child) + li {
        margin-top: 20px;
    }
複製程式碼

  通用選擇器,選擇E後面所有的兄弟節點F。同樣上面的例子:

    li:first-child ~ li {
        margin-top: 20px;
    }
複製程式碼

  如果你能理解上面這兩種寫法,基本上這兩個選擇器你就理解了。

4、偽元素選擇器

  首先對於偽元素的寫法,標準是推薦雙冒號,感覺這樣寫挺好的,可以和偽類區別開來。

  ::first-letter作用於整塊文字的第一段的第一個文字,元素必須為塊級元素。例如修飾段落第一個單詞的樣式:

    p::first-letter {
        color: red;
        font-size: 20px;
    }
複製程式碼

  ::first-line則是作用於整塊文字的第一段,同樣必須為塊級元素。

  ::before和::after分別向元素的前後新增內容,用處很大,比如我們實現這樣的購物車:

購物車
    <span role="btn" aria-label="購物車" attr-quantity="2" class="caricon"></span>
複製程式碼
    /* 這裡就不展現無關緊要的樣式了 */
    .caricon {
        /* 這裡設定容器的樣式 */
    }
    .caricon::before {
        /* 通過偽元素設定購物車圖示 */
    }
    .caricon::after {
        /* 
            這兩個偽元素多有一個重要的content屬性,用來設定偽元素的內容 
            這裡通過attr獲取元素的屬性值,實現購物車數量
        */
        content: attr(attr-quantity);
    }
複製程式碼

  在一些網站中,當你滑鼠選擇文字會發生樣式變化,這是因為用了::selection,不過它能改變的CSS屬性比較有限,具體可以查詢MDN。

  還有我們討厭input的placeholder樣式太丑時,你可以使用::placeholder來修改,但是這是一個試驗性的偽元素,還有很多試驗性的偽元素,有興趣可以搜尋一下。

5、偽類

  偽類的組成就更豐富了,這裡選擇兩個比較重要可能還有點容易混淆的說說。

  首先看看否定偽類選擇器:not,在前面的例子中你已經看到它的身影了,它匹配不符合描述的元素,用處可以說很大。

  然後就是結構偽類選擇器,這裡的內容也多,而且很容易混淆。

  例如:first-child和:first-of-type:

    p:first-child {
        /* 計算範圍:所有的兄弟節點 */
    }
    
    p:first-of-type {
        /* 計算範圍:兄弟節點中所有p元素 */
    }
複製程式碼

  同樣還有:last-child和:last-of-type,就很容易懂了。

  :nth-child就比較強大了,不僅可以使用odd和even,還可以使用表示式。例如我們列表的斑馬線:

    li:nth-child(even) {
        background: red;
    }
複製程式碼

  關於:nth-child重要的幾點可以總結為:

  • 值的下標是從1開始的;
  • 但是n的下標是從0開始;
  • 當表示式的值為0或者負數是無效的;
  • 採用表示式可以實現很多需求,例如我只想讓列表的前三項設定樣式:nth-child(-n + 3);

  像什麼:nth-of-type,:nth-last-child和:nth-last-of-type是不是小case了。

  :only-child表示父節點只有一個子節點,其實學習了上面的一些選擇器,我們完全可以找到等價的表達方式:

    li:first-child:last-child {
        background: yellow;
    }
複製程式碼

  當然還有:only-of-type是必不可少的。

二、選擇器的優先順序

  上面這麼多的選擇器,是不是很頭大,這也是為什麼這裡要特地說一下選擇器優先順序。當多條樣式應用在一個元素上,系統會將樣式合併,當一個屬性被賦了多次值,到底取哪個值呢?這時候就需要優先順序了,優先順序高的覆蓋優先順序低的。

  選擇器優先順序規則:

  • 首先萬用字元是沒有權重的(所以我感覺這點也是不推薦使用它的原因);
  • !important的權重bug級的高;
  • 權重第二高的就是元素的行內樣式(style屬性中的值);

  接下來的情況我們可以數值化:

  • id選擇器的權重為100;
  • 類選擇器、屬性選擇器和偽類選擇器的權重為10;
  • 元素和偽元素的權重為1。
  • 如果優先順序一樣,則後宣告的優先順序高。

  看個例子:

    li {
        width: 300px;
        height: 90px;
        background: red;
    }
    ul li {
        background: yellow;
    }
複製程式碼

  首先對於沒有重複賦值的width和height,並不存在什麼優先順序的問題,而對於background,第一個樣式的優先順序為1,而第二個樣式的優先順序為2,所以將yellow賦值給background。

三、總結

  前面寫了很多廢話,也沒有將選擇器羅列完整,但是基本的構成,大家應該很清晰了。

  對於後面說的選擇器優先順序的計算,並不是說你每次寫完樣式多要求算一下,最重要的目的是:你理解計算規則後,讓你對每種選擇器的應用場景更加清晰,而不是說隨便用,從而造成意想不到的樣式覆蓋問題。

  最後,謝謝大家看完這篇文章。

參考資料:
圖解CSS3
CSS Specificity: Things You Should Know


  喜歡本文的小夥伴們,歡迎關注我的訂閱號超愛敲程式碼,檢視更多內容.

CSS並不簡單–選擇器大家庭

相關文章