整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          web開發中關于CSS 后代選擇器的介紹

          web開發中關于CSS 后代選擇器的介紹

          于web前端開發人員來說,CSS應該不陌生,CSS中的相關知識點也比較重要。今天給大家介紹的是CSS的后代選擇器,關系選擇器,顧名思義,是根據HTML元素在DOM樹中的關系來選擇元素,這些關系包括后代、父子、同胞、相鄰同胞。

          CSS 后代選擇器

          CSS 屬性選擇器詳解

          CSS 子元素選擇器

          后代選擇器(descendant selector)又稱為包含選擇器。

          后代選擇器可以選擇作為某元素后代的元素。

          根據上下文選擇元素

          我們可以定義后代選擇器來創建一些規則,使這些規則在某些文檔結構中起作用,而在另外一些結構中不起作用。

          舉例來說,如果您希望只對 h1 元素中的 em 元素應用樣式,可以這樣寫:

          h1 em {color:red;}

          上面這個規則會把作為 h1 元素后代的 em 元素的文本變為 紅色。其他 em 文本(如段落或塊引用中的 em)則不會被這個規則選中:

          <h1>This is a <em>important</em> heading</h1>
          <p>This is a <em>important</em> paragraph.</p>
          

          親自試一試

          當然,您也可以在 h1 中找到的每個 em 元素上放一個 class 屬性,但是顯然,后代選擇器的效率更高。

          語法解釋

          在后代選擇器中,規則左邊的選擇器一端包括兩個或多個用空格分隔的選擇器。選擇器之間的空格是一種結合符(combinator)。每個空格結合符可以解釋為“... 在 ... 找到”、“... 作為 ... 的一部分”、“... 作為 ... 的后代”,但是要求必須從右向左讀選擇器。

          因此,h1 em 選擇器可以解釋為 “作為 h1 元素后代的任何 em 元素”。如果要從左向右讀選擇器,可以換成以下說法:“包含 em 的所有 h1 會把以下樣式應用到該 em”。

          具體應用

          后代選擇器的功能極其強大。有了它,可以使 HTML 中不可能實現的任務成為可能。

          假設有一個文檔,其中有一個邊欄,還有一個主區。邊欄的背景為藍色,主區的背景為白色,這兩個區都包含鏈接列表。不能把所有鏈接都設置為藍色,因為這樣一來邊欄中的藍色鏈接都無法看到。

          解決方法是使用后代選擇器。在這種情況下,可以為包含邊欄的 div 指定值為 sidebar 的 class 屬性,并把主區的 class 屬性值設置為 maincontent。然后編寫以下樣式:

          div.sidebar {background:blue;}
          div.maincontent {background:white;}
          div.sidebar a:link {color:white;}
          div.maincontent a:link {color:blue;}
          

          有關后代選擇器有一個易被忽視的方面,即兩個元素之間的層次間隔可以是無限的。

          例如,如果寫作 ul em,這個語法就會選擇從 ul 元素繼承的所有 em 元素,而不論 em 的嵌套層次多深。

          因此,ul em 將會選擇以下標記中的所有 em 元素:

          <ul>
          <li>List item 1
          <ol>
          <li>List item 1-1</li>
          <li>List item 1-2</li>
          <li>List item 1-3
          <ol>
          <li>List item 1-3-1</li>
          <li>List item <em>1-3-2</em></li>
          <li>List item 1-3-3</li>
          </ol>
          </li>
          <li>List item 1-4</li>
          </ol>
          </li>
          <li>List item 2</li>
          <li>List item 3</li>
          </ul>
          

          以上就是本篇內容的全部了,這一篇web開發中CSS 框模型概述希望可以對各位web開發者來說啟到幫助。更多web前端學習資料,可以關注“武漢千鋒”微信公眾號領?。?/p>

          、后代選擇器的定義

          在 CSS 中,后代選擇器用于選擇具有特定父元素的元素。它們以符號 “>” 表示。

          二、后代選擇器的語法

          后代選擇器的語法如下:

          css

          parent > child {}

          三、后代選擇器的使用

          后代選擇器可用于:

          * 選擇嵌套的子元素。

          * 限制選擇器的范圍。

          四、后代選擇器的局限性

          * 它們僅選擇直接子元素,不包括孫子元素。

          * 它們會選擇所有具有特定父元素的子元素。

          五、后代選擇器的最佳實踐

          * 盡量避免使用后代選擇器,因為它們會導致代碼過度復雜。

          * 考慮使用其他選擇器,例如通配符或class選擇器。

          六、后代選擇器示例

          css

          nav > ul > li {}

          此代碼選擇具有 nav 元素的直接子元素 ul,并且 ul 元素的直接子元素 li。

          七、結論

          后代選擇器是 CSS 中一個強大的工具,可以用于選擇嵌套的子元素。然而,要記住他們的局限性,并使用其他選擇器來提高代碼可讀性和可維護性。

          SS選擇符目前有下面這幾個:后代選擇符空格( )、子選擇符箭頭(>)、相鄰兄弟選擇符加號(+)、隨后兄弟選擇符彎彎(~)和列選擇符雙管道(||)。其中對于前4個選擇符,瀏覽器支持的時間較早,非常實用,是本章的重點。最后的列選擇符算是“新貴”,與Table等布局密切相關,但目前瀏覽器的兼容性還不足以使它被實際應用,因此就簡單介紹下。

          4.1 后代選擇符空格( )

          后代選擇符是非常常用的選擇符,隨手抓一個線上的CSS文件就可以看到這個選擇符,它從IE6時代就開始被支持了。但即使天天見,也不見得真的很了解它。

          4.1.1 對CSS后代選擇符可能錯誤的認識

          看這個例子,HTML和CSS代碼分別如下:

          <div class="lightblue">
             <div class="darkblue">
                <p>1. 顏色是?</p>
             </div>
          </div>
          <div class="darkblue">
             <div class="lightblue">
                <p>2. 顏色是?</p>
             </div>
          </div>
          .lightblue { color: lightblue; }
          .darkblue { color: darkblue; }

          請問文字的顏色是什么?

          這個問題比較簡單,因為color具有繼承特性,所以文字的顏色由DOM最深的賦色元素決定,因此1和2的顏色分別是深藍色和淺藍色,如圖4-1所示。

          圖4-1 類選擇器與文字顏色

          這個示例配有演示頁面,讀者可以手動輸入https://demo.cssworld.cn/selector/4/1-1.php親自體驗與學習。

          但是,如果把這里的類選擇器換成后代選擇符,那就沒這么簡單了,很多人會搞錯最終呈現的文字顏色:

          <div class="lightblue">
             <div class="darkblue">
                <p>1. 顏色是?</p>
             </div>
          </div>
          <div class="darkblue">
             <div class="lightblue">
                <p>2. 顏色是?</p>
             </div>
          </div>
          .lightblue p { color: lightblue; }
          .darkblue p { color: darkblue; }

          早些年我拿這道題作為面試題,全軍覆沒,無人答對,大家都認為結果是深藍色和淺藍色,實際上不是,正確答案是,1和2全部都是深藍色,如圖4-2所示。

          圖4-2 后代選擇器與文字顏色

          很多人會搞錯的原因就在于他們對后代選擇符有錯誤的認識,當包含后代選擇符的時候,整個選擇器的優先級與祖先元素的DOM層級沒有任何關系,這時要看落地元素的優先級。在本例中,落地元素就是最后的<p>元素。兩個<p>元素彼此分離,非嵌套,因此DOM層級平行,沒有先后;再看選擇器的優先級,.lightblue p和.darkblue p是一個類選擇器(數值10)和一個標簽選擇器(數值1),選擇器優先級的計算值一樣;此時就要看它們在CSS文件中的位置,遵循“后來居上”的規則,由于.darkblue p更靠后,因此,<p>都是按照color:darkblue進行顏色渲染的,于是,最終1和2的文字顏色全部都是深藍色。

          讀者可以手動輸入https://demo.cssworld.cn/selector/4/1-2.php親自體驗與學習。

          有點反直覺,大家可以多琢磨琢磨、消化消化。

          如果覺得已經理解了,可以看看下面這兩段CSS語句,算是一個小測驗。

          例1:此時1和2的文字顏色是什么?

          :not(.darkblue) p { color: lightblue; }
          .darkblue p { color: darkblue; }

          答案:1和2的文字顏色也同樣都是darkblue(深藍色)。因為:not()本身的優先級為0(詳見第2章),所以:not(.darkblue) p和.darkblue p的優先級計算值是一樣的,遵循“后來居上”的規則,.darkblue p位于靠后的位置,因此1和2的文字顏色都是深藍色。

          例2:此時1和2的文字顏色是什么?

          .lightblue.lightblue p { color: lightblue; }
          .darkblue p { color: darkblue; }

          答案:1和2的文字顏色都是lightblue(淺藍色)。因為選擇器.lightblue.lightblue p的優先級更高。

          4.1.2 對JavaScript中后代選擇符可能錯誤的認識

          直接看例子,HTML如下:

          <div id="myId">
             <div class="lonely">單身如我</div>
             <div class="outer">
                <div class="inner">內外開花</div>
             </div>
          </div>

          下面使用JavaScript和后代選擇器獲取元素,請問下面兩行語句的輸出結果分別是:

          // 1. 長度是?
          document.querySelectorAll('#myId div div').length;
          // 2. 長度是?
          document.querySelector('#myId').querySelectorAll('div div').length;

          很多人會認為這兩條語句返回的長度都是1,實際上不是,它們返回的長度值分別是1和3!

          圖4-3是我在瀏覽器控制臺測試出來的結果。

          圖4-3 JavaScript后代選擇器獲取的元素的長度

          第一個結果符合我們的理解,不解釋。為何下一個語句返回的NodeList的長度是3呢?

          其實這很好解釋,一句話:CSS選擇器是獨立于整個頁面的!

          什么意思呢?例如,你在頁面一個很深的DOM元素里面寫上:

          <style>
          div div { }
          </style>

          整個網頁,包括父級,只要是滿足div div這種后代關系的元素,全部都會被選中,對吧,這點大家都清楚的。

          querySelectorAll里面的選擇器同樣也是全局特性。document.querySelector('#myId').querySelectorAll('div div')翻譯過來的意思就是:查詢#myId元素的子元素,選擇所有同時滿足整個頁面下div div選擇器條件的DOM元素。

          此時我們再仔細看看原始的HTML結構會發現,在全局視野下,div.lonely、div.outer、div.inner全部都滿足div div這個選擇器條件,于是,最終返回的長度為3。如果我們在瀏覽器控制臺輸出所有NodeList,也是這個結果:

          NodeList(3) [div.lonely, div.outer, div.inner]

          這就是對JavaScript中后代選擇符可能錯誤的認識。

          其實,要想querySelectorAll后面的選擇器不是全局匹配,也是有辦法的,可以使用:scope偽類,其作用就是讓CSS選擇器的作用域局限在某一范圍內。例如,可以將上面的例子改成下面這樣:

          // 3. 長度是?
          document.querySelector('#myId').querySelectorAll(':scope div div').length;

          則最終的結果就是1,如圖4-4所示。

          圖4-4 :scope偽類下獲取的元素的長度

          關于:scope偽類的更多內容,可以參見第12章。

          4.2 子選擇符箭頭(>)

          子選擇符也是非常常用、非常重要的一個選擇符,IE7瀏覽器開始支持,和后代選擇符空格有點“遠房親戚”的感覺。

          4.2.1 子選擇符和后代選擇符的區別

          子選擇符只會匹配第一代子元素,而后代選擇符會匹配所有子元素。

          看一個例子,HTML結構如下:

          <ol>
             <li>顏色是?</li>
             <li>顏色是?
                <ul>
                   <li>顏色是?</li>
                   <li>顏色是?</li>
                </ul>
             </li>
             <li>顏色是?</li>
          </ol>

          CSS如下:

          ol li {
             color: darkblue;
             text-decoration: underline;
          }
          ol > li {
             color: lightblue;
             text-decoration: underline wavy;
          }

          由于父子元素不同的text-decoration屬性值會不斷累加,因此我們可以根據下劃線的類型準確判斷出不同選擇符的作用范圍。最終的結果如圖4-5所示。

          圖4-5 子選擇符和后代選擇符的測試結果截圖

          可以看到,外層所有文字的下劃線都只有波浪類型,而內層文字的下劃線是實線和波浪線的混合類型。而實線下劃線是ol li選擇器中的text-decoration:underline聲明產生的,波浪線下劃線是ol>li選擇器中的text-decoration:underline wavy聲明產生的,這就說明,ol>li只能作用于當前子<li>元素,而ol li可以作用于所有的后代<li>元素。

          以上就是這兩個選擇符的差異。顯然后代選擇符的匹配范圍要比子選擇符的匹配范圍更廣,因此,同樣的選擇器下,子選擇符的匹配性能要優于后代選擇符。但這種性能優勢的價值有限,幾乎沒有任何意義,因此不能作為選擇符技術選型的優先條件。

          圖4-5配有演示頁面,讀者可以手動輸入https://demo.cssworld.cn/selector/4/2-1.php親自體驗與學習。

          4.2.2 適合使用子選擇符的場景

          能不用子選擇符就盡量不用,雖然它的性能優于后代選擇符,但與其日后帶來的維護成本比,這實在不值一提。

          舉個例子,有一個模塊容器,類名是.cs-module-x,這個模塊在A區域和B區域的樣式有一些差異,需要重置,我們通常的做法是給容器外層元素重新命名一個類進行重置,如.cs-module-reset-b,此時,很多開發者(也沒想太多)就使用了子選擇符:

          .cs-module-reset-b > .cs-module-x { 
             width: fit-content;
          }

          作為過來人,建議大家使用后代選擇符代替:

          /* 建議 */
          .cs-module-reset-b .cs-module-x { 
             position: absolute;
          }

          因為一旦使用了子選擇符,元素的層級關系就被強制綁定了,日后需要維護或者需求發生變化的時候一旦調整了層級關系,整個樣式就失效了,這時還要對CSS代碼進行同步調整,增加了維護成本。

          記住:使用子選擇符的主要目的是避免沖突。本例中,.cs-module-x容器內部不可能再有一個.cs-module-x,因此使用后代選擇符絕對不會出現沖突問題,反而會讓結構變得更加靈活,就算日后再嵌套一層標簽,也不會影響布局。

          適合使用子選擇符的場景通常有以下幾個。

          (1)狀態類名控制。例如使用.active類名進行狀態切換,會遇到祖先和后代都存在.active切換的場景,此時子選擇符是必需的,以免影響后代元素,例如:

          .active > .cs-module-x { 
             display: block;
          }

          (2)標簽受限。例如當<li>標簽重復嵌套,同時我們無法修改標簽名稱或者設置類名的時候(例如WordPress中的第三方小工具),就需要使用子選擇符進行精確控制。

          .widget > li {}
          .widget > li li {}

          (3)層級位置與動態判斷。例如一個時間選擇組件的HTML通常會放在<body>元素下,作為<body>的子元素,以絕對定位浮層的形式呈現。但有時候其需要以靜態布局嵌在頁面的某個位置,這時如果我們不方便修改組件源碼,則可以借助子選擇符快速打一個補?。?/p>

          :not(body) > .cs-date-panel-x {
             position: relative;
          }

          意思就是當組件容器不是<body>子元素的時候取消絕對定位。

          子選擇符就是把雙刃劍,它通過限制關系使得結構更加穩固,但同時也失去了彈性和變化,需要審慎使用。

          4.3 相鄰兄弟選擇符加號(+)

          相鄰兄弟選擇符也是非常實用的選擇符,IE7及以上版本的瀏覽器支持,它可以用于選擇相鄰的兄弟元素,但只能選擇后面一個兄弟。我們將通過一個簡單的例子快速了解一下相鄰兄弟選擇符,HTML和CSS如下:

          <ol>
             <li>1. 顏色是?</li>
             <li class="cs-li">2. 顏色是?</li>
             <li>3. 顏色是?</li>
             <li>4. 顏色是?</li>
          </ol>
          .cs-li + li {
             color: skyblue;
          }

          結果如圖4-6所示。

          圖4-6 相鄰兄弟選擇符測試結果截圖

          可以看到,.cs-li后面一個<li>的顏色變成天藍色了,結果符合我們的預期,因為.cs-li+li表示的就是選擇.cs-li元素后面一個相鄰且標簽是li的元素。如果這里的選擇器是.cs-li+p,則不會有元素被選中,因為.cs-li后面是<li>元素,并不是<p>元素。

          讀者可以手動輸入https://demo.cssworld.cn/selector/4/3-1.php親自體驗與學習。

          4.3.1 相鄰兄弟選擇符的相關細節

          實際開發時,我們的HTML不一定都是整整齊齊的標簽元素,此時,相鄰兄弟選擇符又當如何表現呢?

          1.文本節點與相鄰兄弟選擇符

          CSS很簡單:

          h4 + p {
             color: skyblue;
          }

          然后我們在<h4>和<p>元素之間插入一些文字,看看<p>元素的顏色是否還是天藍色?

          <h4>1. 文本節點</h4>
          中間有字符間隔,顏色是?
          <p>如果其顏色為天藍,則說明相鄰兄弟選擇符忽略了文本節點。</p>

          結果如圖4-7所示,<p>元素的顏色依然為天藍,這說明相鄰兄弟選擇符忽略了文本節點。

          圖4-7 相鄰兄弟選擇符忽略文本節點效果截圖

          2.注釋節點與相鄰兄弟選擇符

          CSS很簡單:

          h4 + p {
             color: skyblue;
          }

          然后我們在<h4>和<p>元素之間插入一段注釋,看看<p>元素的顏色是否還是天藍色?

          <h4>2. 注釋節點</h4>
          <!-- 中間有注釋間隔,顏色是? -->
          <p>如果其顏色為天藍,則說明相鄰兄弟選擇符忽略了注釋節點。</p>

          結果如圖4-8所示,<p>元素的顏色依然為天藍,說明相鄰兄弟選擇符忽略了注釋節點。

          圖4-8 相鄰兄弟選擇符忽略注釋節點效果截圖

          由此,我們可以得到關于相鄰兄弟選擇符的更多細節知識,即相鄰兄弟選擇符會忽略文本節點和注釋節點,只認元素節點。

          上述兩個測試示例均配有演示頁面,讀者可以手動輸入https://demo.cssworld.cn/selector/ 4/3-2.php親自體驗與學習。

          4.3.2 實現類似:first-child的效果

          相鄰兄弟選擇符可以用來實現類似:first-child的效果。

          例如,我們希望除了第一個列表以外的其他列表都有margin-top屬性值,首先想到就是:first-child偽類,如果無須兼容IE8瀏覽器,可以這樣實現:

          .cs-li:not(:first-child) { margin-top: 1em; }

          如果需要兼容IE8瀏覽器,則可以分開處理:

          .cs-li { margin-top: 1em; }
          .cs-li:first-child { margin-top: 0; }

          下面介紹另外一種方法,那就是借助相鄰兄弟選擇符,如下:

          .cs-li + .cs-li { margin-top: 1em; }

          由于相鄰兄弟選擇符只能匹配后一個元素,因此第一個元素就會落空,永遠不會被匹配,于是自然而然就實現了非首列表元素的匹配。

          實際上,此方法相比:first-child的適用性更廣一些,例如,當容器的第一個子元素并非.cs-li的時候,相鄰兄弟選擇符這個方法依然有效,但是:first-child此時卻無效了,因為沒有任何.cs-li元素是第一個子元素了,無法匹配:first-child。用事實說話,有如下HTML:

          <div class="cs-g1">
             <h4>使用:first-child實現</h4>
             <p class="cs-li">列表內容1</p>
             <p class="cs-li">列表內容2</p>
             <p class="cs-li">列表內容3</p>
          </div>
          <div class="cs-g2">
             <h4>使用相鄰兄弟選擇符實現</h4>
             <p class="cs-li">列表內容1</p>
             <p class="cs-li">列表內容2</p>
             <p class="cs-li">列表內容3</p>
          </div>

          .cs-g1和.cs-g2中的.cs-li分別使用了不同的方法實現,如下:

          .cs-g1 .cs-li:not(:first-child) {
             color: skyblue;
          }
          .cs-g2 .cs-li + .cs-li {
             color: skyblue;
          }

          對比測試,結果如圖4-9所示。

          圖4-9 使用:first-child與相鄰兄弟選擇符得到的測試結果對比

          可以明顯看到,相鄰兄弟選擇符實現的方法第一個列表元素的顏色依然是黑色,而非天藍色,說明正確匹配了非首列表元素,而:first-child的所有列表元素都是天藍色,匹配失敗。可見,相鄰兄弟選擇符的適用性要更廣一些。

          讀者可以手動輸入https://demo.cssworld.cn/selector/4/3-3.php親自體驗與學習。

          4.3.3 眾多高級選擇器技術的核心

          相鄰兄弟選擇符最硬核的應用還是配合諸多偽類低成本實現很多實用的交互效果,是眾多高級選擇器技術的核心。

          舉個簡單的例子,當我們聚焦輸入框的時候,如果希望后面的提示文字顯示,則可以借助相鄰兄弟選擇符輕松實現,原理很簡單,把提示文字預先埋在輸入框的后面,當觸發focus行為的時候,讓提示文字顯示即可,HTML和CSS如下:

          用戶名:<input><span class="cs-tips">不超過10個字符</span>
          .cs-tips {
             color: gray;
             margin-left: 15px;
             position: absolute;
             visibility: hidden;
          }
          :focus + .cs-tips {
             visibility: visible;
          }

          無須任何JavaScript代碼參與,效果如圖4-10所示,上圖為失焦時候的效果圖,下圖為聚焦時候的效果圖。

          圖4-10 失焦和聚焦時候的效果圖

          讀者可以手動輸入https://demo.cssworld.cn/selector/4/3-4.php親自體驗與學習。

          這里只是拋磚引玉,更多精彩的應用請參見第9章。

          4.4 隨后兄弟選擇符彎彎(~)

          隨后兄弟選擇符和相鄰兄弟選擇符的兼容性一致,都是從IE7瀏覽器開始支持的,可以放心使用。兩者的實用性和重要程度也是類似的,總之它們的關系較近,有點遠房親戚的味道。

          4.4.1 和相鄰兄弟選擇符區別

          相鄰兄弟選擇符只會匹配它后面的第一個兄弟元素,而隨后兄弟選擇符會匹配后面的所有兄弟元素。

          看一個例子,HTML結構如下:

          <p class="cs-li">列表內容1</p>
          <h4 class="cs-h">標題</h4>
          <p class="cs-li">列表內容2</p>
          <p class="cs-li">列表內容3</p>

          CSS如下:

          .cs-h ~ .cs-li {
             color: skyblue;
             text-decoration: underline;
          }
          .cs-h + .cs-li {
             text-decoration: underline wavy;
          }

          最終的結果如圖4-11所示。

          圖4-11 相鄰兄弟選擇符和隨后兄弟選擇符測試結果對比

          可以看到.cs-h后面的所有.cs-li元素的文字的顏色都變成了天藍色,但是只有后面的第一個.cs-li元素才有波浪線。這就是相鄰兄弟選擇符和隨后兄弟選擇符的區別,匹配一個和匹配后面全部的元素。

          因此,同選擇器條件下,相鄰兄弟選擇符的性能要比隨后兄弟選擇符高一些,但是,在CSS中,沒有一定的數量級,談論選擇器的性能是沒有意義的,因此,關于性能的權重大家可以看淡一些。

          至于其他細節,兩者是類似的,例如,隨后兄弟選擇符也會忽略文本節點和注釋節點。

          讀者可以手動輸入https://demo.cssworld.cn/selector/4/4-1.php查看本示例的測試結果。

          4.4.2 為什么沒有前面兄弟選擇符

          我們可以看到,無論是相鄰兄弟選擇符還是隨后兄弟選擇符,它們都只能選擇后面的元素,我第一次認識這兩個選擇符的時候,就有這么一個疑問:為什么沒有前面兄弟選擇符?

          后來我才明白,沒有前面兄弟選擇符和沒有父元素選擇符的原因是一樣的,它們都受制于DOM渲染規則。

          瀏覽器解析HTML文檔是從前往后,由外及里進行的,所以我們時常會看到頁面先出現頭部然后再出現主體內容的情況。

          但是,如果CSS支持了前面兄弟選擇符或者父元素選擇符,那就必須要等頁面所有子元素加載完畢才能渲染HTML文檔。因為所謂“前面兄弟選擇符”,就是后面的DOM元素影響前面的DOM元素,如果后面的元素還沒被加載并處理,又如何影響前面的元素樣式呢?如果CSS真的支持這樣的選擇符,網頁呈現速度必然會大大減慢,瀏覽器會出現長時間的白板,這會造成不好的體驗。

          有人可能會說,依然強制采取加載到哪里就渲染到哪里的策略呢?這樣做會導致更大的問題,因為會出現加載到后面的元素的時候,前面的元素已經渲染好的樣式會突然變成另外一個樣式的情況,這也會造成不好的體驗,而且會觸發強烈的重排和重繪。

          實際上,現在規范文檔有一個偽類:has可以實現類似父選擇器和前面選擇器的效果,且這個偽類2013年就被提出過,但是這么多年過去了,依然沒有任何瀏覽器實現相關功能。在我看來,就算再過5到10年,CSS支持“前面兄弟選擇符”或者“父選擇符”的可能性也很低,這倒不是技術層面上實現的可能性較低,而是CSS和HTML本身的渲染機制決定了這樣的結果。

          4.4.3 如何實現前面兄弟選擇符的效果

          但是我們在實際開發的時候,確實存在很多場景需要控制前面的兄弟元素,此時又該怎么辦呢?

          兄弟選擇符只能選擇后面的元素,但是這個“后面”僅僅指代碼層面的后面,而不是視覺層面的后面。也就是說,我們要實現前面兄弟選擇符的效果,可以把這個“前面的元素”的相關代碼依然放在后面,但是視覺上將它呈現在前面就可以了。

          DOM位置和視覺位置不一致的實現方法非常多,常見的如float浮動實現,absolute絕對定位實現,所有具有定位特性的CSS屬性(如margin、left/top/right/bottom以及transform)也可以實現。更高級點的就是使用direction或者writing-mode改變文檔流順序。在移動端,我們還可以使用Flex布局,它可以幫助我們更加靈活地控制DOM元素呈現的位置。

          用實例說話,例如,我們要實現聚焦輸入框時,前面的描述文字“用戶名”也一起高亮顯示的效果,如圖4-12所示。

          圖4-12 輸入框聚焦,前面文字高亮顯示的效果圖

          下面給出4種不同的方法來實現這里的前面兄弟選擇符效果。

          (1)Flex布局實現。Flex布局中有一個名為flex-direction的屬性,該屬性可以控制元素水平或者垂直方向呈現的順序。

          HTML和CSS代碼如下:

          <div class="cs-flex">
             <input class="cs-input"><label class="cs-label">用戶名:</label>
          </div>
          .cs-flex {
             display: inline-flex;
             flex-direction: row-reverse;
          }
          .cs-input {
             width: 200px;
          }
          .cs-label {
             width: 64px;
          }
          :focus ~ .cs-label {
             color: darkblue;
             text-shadow: 0 0 1px;
          }

          這一方法主要通過flex-direction:row-reverse調換元素的水平呈現順序來實現DOM位置和視覺位置的不一樣。此方法使用簡單,方便快捷,唯一的問題是兼容性,用戶群是外部用戶的桌面端網站項目慎用,移動端無礙。

          (2)float浮動實現。通過讓前面的<input>輸入框右浮動就可以實現位置調換了。

          HTML和CSS代碼如下:

          <div class="cs-float">
             <input class="cs-input"><label class="cs-label">用戶名:</label>
          </div>
          .cs-float {
             width: 264px;
          }
          .cs-input {
             float: right;
             width: 200px;
          }
          .cs-label {
             display: block;
             overflow: hidden;
          }
          :focus ~ .cs-label {
             color: darkblue;
             text-shadow: 0 0 1px;
          }

          這一方法的兼容性極佳,但仍有不足,首先就是容器寬度需要根據子元素的寬度計算,當然,如果無須兼容IE8,配合calc()計算則沒有這個問題;其次就是不能實現多個元素的前面選擇符效果,這個比較致命。

          (3)absolute絕對定位實現。這個很好理解,就是把后面的<label>絕對定位到前面就好了。

          HTML和CSS代碼如下:

          <div class="cs-absolute">
             <input class="cs-input"><label class="cs-label">用戶名:</label>
          </div>
          .cs-absolute {
             width: 264px;
             position: relative;
          }
          .cs-input {
             width: 200px;
             margin-left: 64px;
          }
          .cs-label {
             position: absolute;
             left: 0;
          }
          :focus ~ .cs-label {
             color: darkblue;
             text-shadow: 0 0 1px;
          }

          這一方法的兼容性不錯,也比較好理解。缺點是當元素較多的時候,控制成本比較高。

          (4)direction屬性實現。借助direction屬性改變文檔流的順序可以輕松實現DOM位置和視覺位置的調換。

          HTML和CSS代碼如下:

          <div class="cs-direction">
             <input class="cs-input"><label class="cs-label">用戶名:</label>
          </div>
          /* 水平文檔流順序改為從右往左 */
          .cs-direction {
             direction: rtl;
          }
          /* 水平文檔流順序還原 */
          .cs-direction .cs-label,
          .cs-direction .cs-input {
             direction: ltr;
          }
          .cs-label {
             display: inline-block;
          }
          :focus ~ .cs-label {
             color: darkblue;
             text-shadow: 0 0 1px;
          }

          這一方法可以徹底改變任意個數內聯元素的水平呈現位置,兼容性非常好,也容易理解。唯一不足就是它針對的必須是內聯元素,好在本案例的文字和輸入框就是內聯元素,比較適合。

          大致總結一下這4種方法,Flex方法適合多元素、塊級元素,有一定的兼容性問題;direction方法也適合多元素、內聯元素,沒有兼容性問題,由于塊級元素也可以設置為內聯元素,因此,direction方法理論上也是一個終極解決方法;float方法和absolute方法雖然比較適合小白開發,也沒有兼容性問題,但是不太適合多個元素,比較適合兩個元素的場景。大家可以根據自己項目的實際場景選擇合適的方法。

          當然,不止上面4種方法,我們一個margin定位也能實現類似的效果,這里就不一一展開了。

          以上4種方法均配有演示頁面,讀者可以手動輸入https://demo.cssworld.cn/selector/4/4-2.php親自體驗與學習。

          4.5 快速了解列選擇符雙管道(||)

          列選擇符是規范中剛出現不久的新選擇符,目前瀏覽器的兼容性還不足以讓它在實際項目中得到應用,因此我僅簡單介紹一下,讓大家知道它大致是干什么用的。

          Table布局和Grid布局中都有列的概念,有時候我們希望控制整列的樣式,有兩種方法:一種是借助:nth-col()或者:nth-last-col()偽類,不過目前瀏覽器尚未支持這兩個偽類;還有一種是借助原生Table布局中的<colgroup>和<col>元素實現,這個方法的兼容性非常好。

          我們通過一個簡單的例子快速了解一下這兩個元素。例如,表格的HTML代碼如下:

          <table border="1" width="600">
             <colgroup>
                <col>
                <col span="2" class="ancestor">
                <col span="2" class="brother">
             </colgroup>
             <tr>
                <td> </td>
                <th scope="col">后代選擇符</th>
                <th scope="col">子選擇符</th>
                <th scope="col">相鄰兄弟選擇符</th>
                <th scope="col">隨后兄弟選擇符</th>
             </tr>
             <tr>
                <th scope="row">示例</th>
                <td>.foo .bar {}</td>
                <td>.foo > .bar {}</td>
                <td>.foo + .bar {}</td>
                <td>.foo ~ .bar {}</td>
             </tr>
          </table>

          可以看出表格共有5列。其中,<colgroup>元素中有3個<col>元素,從span屬性值可以看出,這3個<col>元素分別占據1列、2列和2列。此時,我們給后面2個<col>元素設置背景色,就可以看到背景色作用在整列上了。CSS如下:

          .ancestor {
             background-color: dodgerblue;
          }
          .brother {
             background-color: skyblue;
          }

          最終效果如圖4-13所示。

          圖4-13 表格中的整列樣式控制

          但是有時候我們的單元格并不正好屬于某一列,而是跨列,此時,<col>元素會忽略這些跨列元素。舉個例子:

          <table border="1" width="200">
             <colgroup>
                <col span="2">
                <col class="selected">
             </colgroup>
             <tbody>
                <tr>
                   <td>A</td>
                   <td>B</td>
                   <td>C</td>
                </tr>
                <tr>
                   <td colspan="2">D</td>
                   <td>E</td>
                </tr>
                <tr>
                   <td>F</td>
                   <td colspan="2">G</td>
                </tr>
             </tbody>
          </table>
          col.selected {
             background-color: skyblue;
          }

          此時僅C和E兩個單元格有天藍色的背景色,G單元格雖然也覆蓋了第三列,但由于它同時也屬于第二列,因此被無視了,效果如圖4-14所示。

          圖4-14 G單元格沒有背景色

          這就有問題了。很多時候,我們就是要G單元格也有背景色,只要包含該列,都認為是目標對象。為了應對這種需求,列選擇符應運而生。

          列選擇符寫作雙管道(||),是兩個字符,和JavaScript語言中的邏輯或的寫法一致,但是,在CSS中卻不是“或”的意思,用“屬于”來解釋要更恰當。

          通過如下CSS選擇器,可以讓G單元格也有背景色:

          col.selected || td {
             background-color: skyblue;
          }

          col.selected || td的含義就是,選擇所有屬于col.selected的<td>元素,哪怕這個<td>元素橫跨多個列。

          于是,就可以看到圖4-15所示的效果。

          圖4-15 G單元格有背景色

          本文摘自《CSS選擇器世界》

          這是一本CSS進階書,非常適合有一定CSS基礎的前端人員學習和參考,新手讀起來會有些吃力,因為為了做到內容精練,書中會直接略去過于基礎的知識。

          本書融入了大量我的個人理解,這些理解是我多年持之以恒對CSS進行研究和思考后,經過個人情感潤飾和認知提煉獲得的產物。因此,與干巴巴的教條式的技術書相比,本書要顯得更易于理解,有溫度,更有人文關懷。但是,個人的理解并不能保證百分之百正確,因此,本書的個別觀點也可能不對,歡迎讀者提出質疑和挑戰。

          由于規范尚未定稿,本書部分比較前沿的知識點在未來會發生某些小的變動,我會實時跟進,并在官方論壇同步更新。


          主站蜘蛛池模板: 99久久精品国产高清一区二区 | 无码人妻精品一区二区| 国产人妖在线观看一区二区| 濑亚美莉在线视频一区| 国产乱码精品一区二区三区四川| 精品国产香蕉伊思人在线在线亚洲一区二区| 日韩av片无码一区二区三区不卡| 国产av天堂一区二区三区| 亚洲视频一区二区三区四区| 高清一区二区三区视频| 精品三级AV无码一区| 一区二区三区免费电影| 精品无码国产AV一区二区三区| 日韩精品无码人妻一区二区三区 | 一区二区三区日韩精品| 在线视频亚洲一区| 国产高清不卡一区二区| 精品视频一区二区观看| 极品人妻少妇一区二区三区| 国产一区二区视频在线播放| 欧亚精品一区三区免费| 任你躁国产自任一区二区三区 | 精品一区二区三区免费观看 | 无码人妻精品一区二区三区在线| 韩国精品一区视频在线播放| 成人中文字幕一区二区三区| 国产日韩一区二区三免费高清| 男人的天堂av亚洲一区2区| 亚洲一区中文字幕| 久久高清一区二区三区| 日韩欧国产精品一区综合无码| 人妻内射一区二区在线视频| 精品人妻一区二区三区四区 | 日韩精品无码Av一区二区| 国产一区高清视频| 久久久久人妻一区精品| 一夲道无码人妻精品一区二区| 综合无码一区二区三区| 久久精品一区二区三区资源网| 国产在线精品一区二区中文| 一区二区三区影院|