首頁  >  文章  >  web前端  >  深入了解CSS中的選擇器

深入了解CSS中的選擇器

青灯夜游
青灯夜游轉載
2021-07-22 11:15:002629瀏覽

本篇文章帶大家了解CS​​S中的選擇器,介紹一下簡單選擇器、複合選擇器、複雜選擇器的語法,選擇器優先權。

深入了解CSS中的選擇器

選擇器語法

#我們先了解選擇器的語法,然後再深入了解背後相關的特性。

簡單選擇器

  • #星號 —— *
    • 通用選擇器,可以選擇任何的元素
  • 類型選擇器|type selector —— div svg|a
    • 也叫做type selector, 也就是說它選擇的是元素中的tagName (標籤名) 屬性
    • tagName 也是我們平常最常用的的選擇器
    • 但因為HTML 也是有命名空間的,它主要有三個:HTMLSVGMathML
    • 如果我們想選SVG 或MathML 裡面特定的元素,我們就必須要用到單豎線| ,CSS選擇器裡面單豎線是一個命名空間的分隔符,而HTML 裡面命名空間分隔符是冒號: 。然後前面說到的命名空間是需要@namespace 來聲明的,他們是配合使用的,但是這個命名空間的使用不是很頻繁,它的存在只是為了一個完備性考慮,HTML 和SVG當中唯一一個重疊的元素名就只有一個a
    • 所以我們可以認為,類型選擇器就是一個簡單的文字字串即可
  • #類別選擇器|class selector —— .class-name#
    • . 開頭的選擇器就是class 選擇器,也是最經典之一
    • 它會選擇一個class,我們也可以用空格做分隔符號來製定多個class 的
    • 這個.class 只要符合其中一個就可以了
  • ID 選擇器|id selector —— #id
    • # 開頭加上ID 位元選取一個ID
    • 這個是嚴格符合的
    • ID 裡面是可以加減號或是其他符號的
  • 屬性選擇器|attribute selector —— [attr=value]
    • 它包含了class 屬性選擇器和id 選擇器
    • 這個選擇器的完整語法就是attr=value,等於前面是屬性名,後面是屬性值
    • 這裡面的等號前面可以加 就表示像class 一樣,可以支援拿空格分隔的值的序列:attr~=value
    • 如果在等號前面加單豎線,表示這個屬性以這個值開頭即可:attr|=value
    • 如果我們對優先權沒有特殊要求的話,我們理論上是可以用屬性選擇器來取代class 選擇器和id 選擇器的
  • #偽類別 —— :hover
    • : 開頭的,它主要是一些屬性的特殊狀態
    • 這個跟我們寫的HTML 沒有關係,多半來自於互動和效果
    • 一些偽類選擇器是帶有函數的偽類選擇器,這些我們都是可以去使用偽類來解決的
  • #偽元素選擇器 —— ::before
    • 一般來說是以:: 雙冒號開頭的
    • 其實是支援使用單冒號的,但是我們提倡雙冒號這個寫法
    • 因為我們可以一眼就看出這個是偽元素選擇器,和偽類別區分開來
    • 偽元素屬於選中一些原本不存在的元素
    • 如果我們不選擇它們,這個地方就不存在這個元素了,選擇後就會多了一個元素

#複合選擇器

  • * 或則div 必須寫在最前面

首先複合選擇器是以多個簡單選擇器構成的,只要把簡單選擇器挨著寫就變成一個複合選擇器了。它的語意就是我們選取的元素必須同時 match 幾個簡單選擇器,形成了 「與」 的關係。

複雜選擇器

#複合選擇器中間用連接符號就可以變成複雜選擇器了,複雜選擇器是針對一個元素的結構來進行選擇的。

  • —— 子孫選擇器,單一元素必須要有空格左邊的一個父級節點或祖先節點
  • # “>” —— 父子選擇器,必須是元素直接的上級父元素
  • “~” —— 鄰接關係選擇器
  • “ ” —— 鄰接關係選擇器
  • # “||” —— 雙垂直線是Selector Level 4 才有的,當我們做表格的時候可以選取每一個欄位

選擇器優先權

在先前的《實戰中學習瀏覽器運作原理》中也接觸過選擇器的優先權的概念了。這裡我們深入了解一下選擇器優先順序的概念。

簡單選擇器計數

我們從一個案例出發,選擇器優先權是對一個選擇器裡麵包含的所有簡單選擇器進行計數。所以選擇器清單不被視為一個完整的選擇器(也就是逗號分隔的選擇器),因為選擇器清單中間是以逗號分隔開的複雜選擇器來進行簡單選擇器計數的。

範例:#id ​​div.a#id

  • #這個裡麵包含了兩個ID 選擇器,一個類型選擇器和一個class 選擇器
  • 根據一個specificity 陣列的計數[inline-style個數,ID 選擇器個數,class 選擇器個數,tagName 選擇器個數]
  • 我們這個範例就會得到specificity = [0, 2, 1, 1]
  • 在選擇器的標準裡面,有一個這樣的描述,我們會採用一個N 進位來表示選擇器優先權
  • 所以 深入了解CSS中的選擇器
  • 我們只需要取一個大的N,算出來就是選擇器的優先權了
  • #比如說我們用 N=1000000,那麼S=2000001000001#,這個就是這個範例中選擇器的specificity 優先權了

像IE 的舊版IE6,因為為了節省記憶體N 取值不夠大,取了一個255 為N 的值,所以就發生了非常好玩的事情,比如說到值我們256 個class 就相當於一個ID。後來我們大部分的瀏覽器都選擇了 65536,基本上就再也沒有發生過超過額度的事情了。因為標準裡面只說採用一個比較大的值就可以,但是我們要考慮記憶體暫用的問題,所以我們會取一個16 進位上比較整的數,一般來說都是256 的整次方(因為256 是剛好是一個位元組)。

CSS 偽類別

#偽類別其實是一類非常多的內容的簡單選擇器。

連結/行為

#
  • :any-link —— 可以符合任何的超連結
  • :link —— 還沒造訪過的超連結
  • :link :visited —— 符合所有被造訪過的超連結
  • :hover —— 使用者滑鼠放在元素上之後的狀態,之前是只能對超連結生效,但是現在是可以在很多元素中使用了
  • :active —— 之前也是只對超連結生效的,點擊之後當前的連結就會生效
  • :focus —— 就是焦點在這個元素中的狀態,一般用於input 標籤,其實任何可以獲得焦點的元素都可以使用
  • :target —— 連結到目前的目標,這個不是給超連結用的,是給錨點的a 標籤使用的,就是目前的HASH指向了目前的a 標籤的話就會啟動target 偽類別

一旦使用了:link:visited 之後,我們就再也無法對這個元素的文字顏色之外的屬性進行更改。為什麼要這樣設計呢?因為一旦我們使用了 layout 相關的屬性,比如說我們給 :visited 的尺寸加大一點,它就會影響排班。這樣我們就可以透過 JavaScript 的 API 去取得這個連結是否被存取過了。但是如果我們能獲得連結是否被訪問過了,那麼我們就可以知道用戶訪問過那些網站了,這個對於瀏覽器的安全性來說是一個致命打擊。所以這裡也提醒一下大家,不要以為做一些表現性的東西於安全沒有任何關係,其實安全性是個綜合的考量。 CSS 它也能造成安全漏洞的。

樹結構

  • #:empty —— 這個元素是否有子元素
  • :nth-child() —— 是父元素的第幾個兒子(child)
  • :nth-last-child() — — 於nth-child 一樣,只不過從後往前數
  • #:first-child :last-child :only-child

##:nth-child 是一個非常複雜的偽類,裡面支援一種文法,比如說​​可以在括號裡面寫奇偶eventodd ,也可以寫4N 13N-1

,這個就會分別配對到整數的形態。因為這個是比較複雜的選擇器,我們就不要在裡面寫過於複雜的表達式了,只用它來處理一下奇偶,逢3個多1個,逢4個多1個等等這種表達式。

其實empty

nth-last-child
  • last-child
  • only-child
  • 這兩個選擇器,是破壞了我們先前在《實作中學習瀏覽器原理》中的說到的CSS 計算的時機問題。我們可以想像一下,當我們在開始標籤計算的時候,肯定不知道它有沒有子標籤。
empty
影響不是特別大,但

last-child 的這個關係其實還是影響蠻大的。所以瀏覽在實現這些的時候是做了特別處理的,要嘛就是瀏覽器實現的不是特別好,要嘛就是瀏覽器要耗費更大的效能來實現。所以建議大家盡量避免大量使用這些。

邏輯型

#:not 偽類別- 主流瀏覽器只支援簡單選擇器的序列(複合選擇器)我們是沒有辦法在裡面寫複雜選擇器的語法的

:where :has —— 在CSS Level 4 加入了這兩個非常強大了邏輯型偽類

  • 這裡還是像溫馨建議一下大家,
  • 不建議大家把選擇器寫的過於複雜
  • 我們很多時候都可以多加一點class 去解決的
  • 。如果我們的選擇器寫的過於複雜,某種程度上意味著 HTML 結構寫的不合理。我們不光是為了給瀏覽器工程省麻煩,也不光是為了效能,而是為了我們自身的程式碼結構考慮,所以我們不應該出現過於複雜的選擇器。

CSS 偽元素#一共分為4 種##::before ::after::first-line

::first-letter

::before

和### ::after### 是在元素的內容的前後,插入一個偽元素。一旦應用了 before 和 after 的屬性,declaration(宣告)裡面就可以寫一個叫做 ###content### 的屬性(一般元素是沒有辦法寫 content 的屬性的)。 ###content### 的屬性就像一個真正的 DOM 元素一樣,可以去產生盒,可以參與後續的排版和渲染了。所以我們可以給他宣告 ###border###、###background###等這樣的屬性。 #########可以理解為:###偽元素在介面上加入了一個不存在的元素。 ######

::first-line::first-letter 的機制就不一樣了。這兩個其實原本就存在 content 之中。他們顧名思義就是 選中“第一行” 和選中 “第一個字母”。它們 不是一個不存在的元素,是把一部分的文字括了起來讓我們可以對它做一些處理。

before 和after

在我們概念裡,我們可以認為帶有before 偽元素的選擇器,會給他實際選取的元素的內容前面增加了一個元素,我們只需要透過他的content 屬性為它添加文字內容。 (這裡我們也可以賦予偽元素 content: '' 為空的)所以我們可以任何的給 before 和 after 指定 display 屬性,和不同元素一樣比較自由的。

我們在實作一些組成的時候,也會常常使用這種不污染 DOM 樹,但是能實際創造視覺效果的方式來為頁面添加一些修飾性的內容。

<div>
  <::before/>
  content content content content
  content content content content
  content content content content
  content content content content
  <::after/>
</div>

first-letter 和first-line

first-letter 相當於我們有一個元素把內容裡面的第一個字母給括了起來。這個 first-letter 我們是可以任意宣告各種不同的屬性的,但是我們是無法改變它的 content 的。我們應該都看過報紙上的第一個字母會比較大,然後會遊離出來的效果,這個在 CSS 裡面我們就可以用 ::first-letter的偽元素選擇器了。使用這個來實現相比用 JavaScript 來實現就會更加穩定和程式碼更加優雅一些。

<div>
  <::first-letter>c</::first-letter>ontent content content content
  content content content content
  content content content content
  content content content content
</div>

first-line 是針對排版之後的 line,其實跟我們原始碼裡面的 first line 沒有任何的關係的。假如說我們的瀏覽器提供的渲染的寬度不同,first-line 在兩個環境裡面它最終括住的元素數量就不一樣多了。所以我們用這個選擇器的時候需要去根據需求的情況使用,很有可能在我們開發機器上和使用者的機器上渲染出來的效果是不一樣的!

<div>
  <::first-line>content content content content content</::first-line>
  content content content content
  content content content content
  content content content content
</div>

這兩個選擇器其實可用的屬性也是有區別的:

first-line 可用屬性

  • font 系列
  • color 系列
  • #background 系列
  • word-spacing
  • letter- spacing
  • text-decoration
  • ##text-transform
  • #line-height

first-letter 可用屬性

    font 系列
  • color 系列
  • background 系列
  • text-decoration
  • text-transform
  • letter-spacing
  • word-spacing
  • line-height
  • float
  • vertical-align
  • #盒模型系列:margin, padding, border

小練習

#寫一個match 函數。它接受兩個參數,第一個參數是一個選擇器字串性質,第二個是一個 HTML 元素。這個元素你可以認為它一定會在一棵 DOM 樹裡面。透過選擇器和 DOM 元素來判斷,目前的元素是否能夠匹配到我們的選擇器。 (不能使用任何內建的瀏覽器的函數,僅透過 DOM 的 parent 和 children 這些 API,來判斷一個元素是否能夠跟一個選擇器相符。)以下是一個呼叫的範例。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Match Example —— by 三钻</title>
  </head>
  <body>
    <div>
      <b>
        <div class="class classA" id="id">content</div>
      </b>
    </div>
  </body>
  <script language="javascript">
    /**
     * 匹配选择器
     */
    function matchSelectors(selector, element) {
      // 先匹配当前元素是否匹配
      let tagSelector = selector.match(/^[\w]+/gm);
      let idSelectors = selector.match(/(?<=#)([\w\d\-\_]+)/gm);
      let classSelectors = selector.match(/(?<=\.)([\w\d\-\_]+)/gm);

      /**
       * 实现复合选择器,实现支持空格的 Class 选择器
       * --------------------------------
       */
      // 检查 tag name 是否匹配
      if (tagSelector !== null) {
        if (element.tagName.toLowerCase() !== tagSelector[0]) return false;
      }
      // 检测 id 是否匹配
      if (idSelectors !== null) {
        let attr = element.attributes[&#39;id&#39;].value;
        if (attr) {
          for (let selector of idSelectors) {
            if (attr.split(&#39; &#39;).indexOf(selector) === -1) return false;
          }
        }
      }
      // 检测 class 是否匹配
      if (classSelectors !== null) {
        let attr = element.attributes[&#39;class&#39;].value;
        if (attr) {
          for (let selector of classSelectors) {
            if (attr.split(&#39; &#39;).indexOf(selector) === -1) return false;
          }
        }
      }

      return true;
    }

    /**
     * 匹配元素
     */
    function match(selector, element) {
      if (!selector || !element.attributes) return false;

      let selectors = selector.split(&#39; &#39;).reverse();

      if (!matchSelectors(selectors[0], element)) return false;

      let curElement = element;
      let matched = 1;

      // 递归寻找父级元素匹配
      while (curElement.parentElement !== null && matched < selectors.length) {
        curElement = curElement.parentElement;
        if (matchSelectors(selectors[matched], curElement)) matched++;
      }

      // 所有选择器匹配上为 匹配成功,否则是失败
      if (matched !== selectors.length) return false;

      return true;
    }

    let matchResult = match(&#39;div #id.class&#39;, document.getElementById(&#39;id&#39;));
    console.log(&#39;Match example by 三钻&#39;);
    console.log(&#39;matchResult&#39;, matchResult);
  </script>
</html>
更多程式相關知識,請造訪:

程式設計影片! !

以上是深入了解CSS中的選擇器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除