CSS基本功從頭練之Selector

接灰的電子產品發表於2017-01-17

CSS一直是我的短板,從來沒有系統學過,一直都是使用第三方的樣式庫,或者在網上找點資料copy。最近感覺應該系統的梳理一下,否則很多概念還是不太清晰。還是屬於邊學邊寫的資料,難免各種漏洞,希望大家多指正。

Selector(選擇器)

為什麼會有選擇器這個概念?因為樣式最終是要應用到HTML的元素上的,那麼哪一類樣式應該應用到什麼元素這個問題就催生了選擇器的誕生。總體來說,Selector分標籤選擇器、類選擇器、ID選擇器和屬性選擇器

標籤選擇器

顧名思義是以HTML標籤作為篩選依據的,比如下面的程式碼中,我們的可見標籤一共有:<html><body><div><label><input><output>

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div>
    <label for="weight">體重</label>
    <input id="weight" type="number">
  </div>
  <div>
    <label for="height">身高</label>
    <input id="height" type="number">
  </div>
  <div>
    <label for="bmi">BMI</label>
    <output id="bmi"></output>
  </div>

</body>
</html>複製程式碼

如果我們在css中有下面的定義,那麼我們其實是對所有 <body> 標籤(雖然事實上只有一個)應用樣式背景色 #DCEDC8,而對所有 <input> 標籤應用樣式背景色 #8BC34A

body {
  background-color: #DCEDC8;
}

input {
  background-color: #8BC34A;
}複製程式碼

CSS基本功從頭練之Selector
應用body和input標籤樣式的效果

類選擇器

類選擇器的篩選標準是用類來確定的,就是在html標籤中,我們一般可以用 class="blablabla" 來完成樣式的類指定。現在我們改寫HTML,把所有label指定一個 class="label"

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div>
    <label class="label" for="weight">體重</label>
    <input id="weight" type="number">
  </div>
  <div>
    <label class="label" for="height">身高</label>
    <input id="height" type="number">
  </div>
  <div>
    <label class="label" for="bmi">BMI</label>
    <output id="bmi"></output>
  </div>

</body>
</html>複製程式碼

然後在css中加上一個類樣式定義,在css中我們在類名的前面加上一個 . 來表示這是個類樣式定義。

.label {
  color: #fff;
}複製程式碼

CSS基本功從頭練之Selector
使用label類樣式定義後的效果

ID選擇器

我們看到上面的HTML程式碼中,有一些元素是有 id 這個屬性的,那麼這個選擇器當然就是用來選擇 id="blablabla" 的元素的。如果我們在css中給BMI結果加入一個ID選擇器,放大結果的字型。

#bmi {
  font-size: xx-large;
}複製程式碼

CSS基本功從頭練之Selector
應用了ID選擇器的效果

屬性選擇器

屬性選擇器就是對HTML標籤中含有的屬性進行篩選,比如上例中我們如果想對標籤中 for 屬性、值為weight的進行特殊處理,那麼我們可以看到下圖的效果。

label[for="weight"] {
  color: #FF4081;
}複製程式碼

CSS基本功從頭練之Selector
應用屬性選擇器的效果

其實我感覺前面的ID選擇器、類選擇器甚至標籤選擇器都應該是屬性選擇器的一種特殊形式,比如我們其實把 #bmi 那段程式碼註釋掉,換成屬性選擇器:選擇 id="bmi" 的形式效果是一樣的。

/* #bmi {
  font-size: xx-large;
} */

output[id="bmi"] {
  font-size: xx-large;
}複製程式碼

屬性選擇器的花樣最多,下面我們來看看,如果不用 =,而使用 ~= 是什麼效果。我們把bmi的label中的for改成 for="weight height"

  <div>
    <label class="label" for="weight height">BMI</label>
    <output id="bmi"></output>
  </div>複製程式碼

然後在css中用 ~= 替代 = 看看會發生什麼?我們看到體重和BMI都應用了 #FF4081 這個顏色。也就說 ~= 表示選擇所有含有等號後面的值的元素。

label[for~="weight"] {
  color: #FF4081;
}複製程式碼

CSS基本功從頭練之Selector
使用 `~=` 代表選擇含有該值的所有元素

但是注意一點,~= 後面的一定是一個完整的屬性值,而不是含有這個字串就行,比如上面HTML中如果我們寫成 for="weight1 height" 那麼這個選擇器就無法生效了。

css1中還支援一種符號 |=,它會選擇所有以等號後的值開頭的元素,同樣的,這裡的開頭指的是完整屬性值而不是字串意義上的開頭。那麼問題來了,有沒有辦法選擇字串呢?css3中增加了 ^=$=*= 這幾個符號,是專門處理字串這種需求的。題外話感覺這幾個操作符很像正規表示式中的。

還是上面的例子,我們的html如下,注意bmi的label的for中是 weight1,而體重label的for中是 weight

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <script src="https://unpkg.com/@reactivex/rxjs@5.0.0-beta.7/dist/global/Rx.umd.js"></script>
  <title>JS Bin</title>
</head>
<body>
  <div>
    <label class="label" for="weight">體重</label>
    <input id="weight" type="number">
  </div>
  <div>
    <label class="label" for="height">身高</label>
    <input id="height" type="number">
  </div>
  <div>
    <label class="label" for="weight1 height">BMI</label>
    <output id="bmi"></output>
  </div>

</body>
</html>複製程式碼

css中我們使用 ^= 來試驗一下,你會發現選擇器又生效了。

label[for^="weight"] {
  color: #FF4081;
}複製程式碼

CSS基本功從頭練之Selector
使用 `^=` 選擇以字串xxx開頭的元素

同樣的如果我們換成下面的css,你會發現所有的label都變色了,因為都是 eight這個字串結尾的

label[for$="eight"] {
  color: #FF4081;
}複製程式碼

CSS基本功從頭練之Selector
`$=`選擇以字串xxx結尾的所有元素

這時你會問,不對啊,bmi的label是 weight1 啊,但是請看仔細 for="weight1 height",這個字串的最後是 height

*= 是選擇字串中包含某個值的元素,遇到它的話我比較好奇的是它能不能選擇空格,因為 for="weight1 height"中含有一個空格。於是我做了個實驗,用如下的css證明了是可以選擇空格的。

label[for*=" "] {
  color: #FF4081;
}複製程式碼

CSS基本功從頭練之Selector
`*=`選擇包含xxx字串的元素

上下文選擇器

如果僅有上面這些也還不錯,css的強大或者說複雜在於還特麼能組合。這個上下文選擇器可以看作組合的一種,它利用多種符號連線標籤選擇器來實現根據上下文選擇元素的目的。

還是用例項說話,首先最簡單的組合符號 ,,為了看的更清楚一些,我們重新定義HTML:在最外面包了一層 div,並且給每一層 div 都指定了一個樣式類。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <script src="https://unpkg.com/@reactivex/rxjs@5.0.0-beta.7/dist/global/Rx.umd.js"></script>
  <title>JS Bin</title>
</head>
<body>
  <div class="content">
    <div class="weight">
      <label class="label" for="weight">體重</label>
      <input id="weight" type="number">
    </div>
    <div class="height">
      <label class="label" for="height">身高</label>
      <input id="height" type="number">
    </div>
    <div class="bmi">
      <label class="label" for="weight1 height">BMI</label>
      <output id="bmi"></output>
    </div>
  </div>
</body>
</html>複製程式碼

css 中為幾個 div 定義背景色,以便看的更清楚些。然後利用 , 定義了 div,label 的padding是 20px

...
.weight {
  background-color: gray;
}

.height {
  background-color: silver;
}

.bmi {
  background-color: darkgray;
}

.content{
  background-color: brown;
}

div,label {
  padding: 20px;
}複製程式碼

, 這種就比較簡單,就是多個標籤都使用這個樣式,我們來看看效果:

CSS基本功從頭練之Selector
採用了`,`表示“和”

CSS基本功從頭練之Selector
只有div時的效果,仔細看會發現文字靠左邊緣近了一些

CSS基本功從頭練之Selector
只有label時的效果

這個操作符我認為只起到節省程式碼的作用;-)

後代選擇器 (空格)

下面我們看看 (空格)這個操作符,還是為了更明顯,我們在HTML中的最外層div之外加一行

<label for="something">我不在div中,所以沒有變大</label>複製程式碼

我們在css中加上下面的定義,我們會看到,div內部的所有label的文字都變大了,但外部的沒有。所以 x y表示選擇在x元素內部的所有y

div label {
  font-size: x-large;
}複製程式碼

CSS基本功從頭練之Selector
` `空格表示在div內部的所有label

子選擇器 >

那麼下面我們看看 >,同樣需要改造一下HTML,在 <div class="content"> 下加入下面的程式碼。

<p>
  <label for="something">我的父節點不是div,所以沒有變大</label>
</p>複製程式碼

將css改成

div>label {
  font-size: x-large;
  padding:20px;
}複製程式碼

下面看看效果,剛剛加入的那個label文字沒有變大的原因在於它的父節點是 <p>

CSS基本功從頭練之Selector
`div>label` 符號篩選父節點為div的label元素

“親兄弟”選擇器 +

接下來的這位就有意思了 +,你如果把 div+label 直接套用到上面的HTML中,會發現沒什麼作用,哪個字型也沒變大。原因是這個符號是選擇div 之後緊挨著 div的label元素。

那麼我們的實驗之前,請把下面的程式碼插入到最外層div之後

<label for="something">我不在div中但緊挨著div,所以變大了</label>
<label for="something">我雖然在div後但沒有緊挨著div,所以沒有變大了</label>複製程式碼

CSS基本功從頭練之Selector
`+` 指的是緊挨著前面的元素的所有元素

"兄弟連"選擇器 ~

有看後面的就有看前面的,接下來就是 ~ 閃亮登場了,div~label 是個什麼效果捏? 事實證明 x~y 是用於選擇前面有 x 的所有 y

div~label {
  font-size: x-large;
  padding:20px;
}複製程式碼

CSS基本功從頭練之Selector
`div~label` 用於選擇之前有div的所有label

偽類

為什麼叫這個名字?我也不知道,感覺有點怪,聽說一個說法是它們很像類,但我真沒看出哪兒像。偽類一般都是以 : 開頭,也不光是偽類的,偽元素也是 : 開頭。

連結偽類

首先看連結偽類,這個其實沒什麼好講,就是因為連結有幾個狀態,普通的選擇器無法篩選出來導致硬生生的造出來這幾個東東。需要注意的是,這些貨必須按著下面的順序給出才可以生效。其實我實驗了一下,link和visited調換位置沒影響,其他按順序是因為這幾個狀態有時有重疊,寫錯順序會導致樣式被覆蓋。比如滑鼠按下未抬起其實同時也滿足hover的條件,如果我們把hover放在最後,active放在前面,那麼這時就會以最後的樣式(hover)覆蓋前面的樣式(active)

  • link: 沒有訪問過也沒有滑鼠懸停或點選的狀態。
  • visited:使用者點選過這個連結之後的狀態。
  • hover:滑鼠指標正懸停在連線上。
  • active:連結正在被點選(滑鼠在元素上按下,還沒有釋放)。

“選中狀態”偽類

再來看 :focus 偽類,這個也是由於輸入框處於聚焦狀態時無法通過其他選擇器選取而特別定製的,我們看一下效果

input:focus {
  background-color: #C5CAE9;
}複製程式碼

CSS基本功從頭練之Selector
體重輸入框聚焦的狀態

同理,還有類似的對於核取方塊、單選框等選中狀態的選擇所設計的 :checked;為元素禁用和啟用狀態所設計的 :enabled:disabled。這些就不舉例了,比較簡單,如果有坑,請指教。

“排頭兵”偽類

:first-letter:first-line:first-child 這幾個跟在某個元素後面的作用是選取該元素的第一個文字(英文的話是字母)、第一行和父元素的第一個子元素中的所有該元素。還是看圖說話吧。
我們把體重的selector加了一個首字母偽類放大字型200%,效果如下圖,另兩個的效果類似,只不過是第一行或者第一個子元素,自己可以實驗一下。

.weight:first-letter {
  font-size:200%;
}複製程式碼

CSS基本功從頭練之Selector
首字大寫的效果

“字首”和“字尾”偽類

為什麼叫它們“字首”和“字尾”呢?因為它們是作用於選擇內容之前或之後的,為什麼要設計這個選擇器呢?我們的BMI計算器目前的兩個輸入值都是沒有單位的。如果我們希望增加一個單位的話,當然可以在HTML頁面增加,但是如果對於某一類值它們的單位就是某些文字的話,這麼做就有點累。我們給css加上下面的樣式看看效果。

.weight::after {
  content: "公斤";
}

.height::after {
  content: "釐米";
}複製程式碼

另外一個比較常見的技巧是利用這個偽類去改造預設的控制元件樣式,比如核取方塊預設是方塊型,如何改造成圓形呢?我們就可以先把核取方塊中的原有表現形式(方塊)隱藏起來,然後利用“字尾”偽類畫出一個圓來。

//html
<input type="checkbox" class="check">
//css
.check {
  background-color: #8BC34A;
  width: 40px;
  -webkit-appearance: none;
  appearance: none;
}
.check::after {
  content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#fff" stroke-width="3"/></svg>');
}複製程式碼

CSS基本功從頭練之Selector
圓形核取方塊

“正著數和倒著數”偽類

css增加一堆很類似陣列或集合中常用的選擇器,這一類的大部分都是成對的,可以記成正著數和倒著數。為了方便記憶,我就起名叫“正著數和倒著數”偽類了。這一類有好多,包括選擇首尾的: :first-of-type:last-of-type:first-child(這個不是css3引入的),:last-child;第n個子元素的 :nth-child(n):nth-last-child(n);第n個某型別子元素:nth-of-type(n):nth-last-of-type(n);當然對於特殊情況,比如只有一個的會有專門的處理::only-child:only-of-type

應用這種選擇器可以非常方便的打造一些效果,比如交替顏色的表格。

table {
    width: 100%;
}

tr:nth-child(even){background-color: #f2f2f2}複製程式碼

CSS基本功從頭練之Selector
交錯顏色的表格

這裡有問題了,這個 even 是怎麼回事?這一類Selector是可以接受表示式作為引數的,而 evenodd 作為兩個特別常用的東東被認定成關鍵字,其實 nth-child(even) 可以改寫成 nth-child(2n)nth-child(odd)可以改寫成 nth-child(2n+1)。是的,如果你願意可以寫出3的倍數,5的倍數等等序列等等。

相關文章