天天看點

CSS魔法堂:一起玩透僞元素和Content屬性

前言

 繼上篇《

CSS魔法堂:稍稍深入僞類選擇器

》記錄完僞類後,我自然而然要向僞元素伸出“魔掌”的啦^_^。本文講講述僞元素以及功能強大的Contet屬性,讓我們可以通過僞元素更好地實作更多的可能!

初識僞元素

 說起僞元素我第一想到的莫過于

::before

::after

這兩個了,它倆其實就是在其附屬的選擇器命中的元素上插入第一個子節點和追加最後一個子節點。那這時我不禁地想問:“直接添加兩個class為.before和.after不是一樣的嗎?”

 其實使用僞元素

::before

::after

以下兩個好處:

  1. HTML的代碼量減少,對SEO有幫助;
  2. 提高JavaScript查詢元素的效率。

 那為什麼會這兩好處呢?原因就是僞元素并不存在于DOM中,而是位于CSSOM,HTML代碼和DOM Tree中均沒有它的身影,量少了自然效率有所提升。但這也引入一個問題——我們沒辦法通過JavaScript完全操控僞元素(我将在下面一節為大家講述)

一大波僞元素來了

除了

::before

::after

外,别漏了以下的哦!

  1. :first-line

    :隻能用于塊級元素。用于設定附屬元素的第一個行内容的樣式。可用的CSS屬性為

    font,color,background,word-spacing,letter-spacing,text-decoration,vertical-align,text-transform,line-height,clear

  2. :first-letter

    :隻能用于塊級元素。用于設定附屬元素的第一個字母的樣式。可用的CSS屬性為

    font,color,background,marin,padding,border,text-decoration,vertical-align,text-transform,line-height,float,clear

  3. ::selection

    :比對選中部分的内容。可用的CSS屬性為

    background,color

有沒有發現有的僞元素字首是

:

有的卻是

::

呢?

::

是CSS3的寫法,其實除了

::selection

外,其他僞元素既兩種字首都是可以的,為相容性可選擇使用

:

,為容易區分僞元素和僞類則使用

::

,但我還是建議使用

::

來提高可讀性,相容性就讓postcss等工具幫我們處理就好了。

::before

::after

的注意事項

  1. 預設

    display: inline

  2. 必須設定content屬性,否則一切都是無用功;
  3. user-select: none

    ,就是

    ::before

    ::after

    的内容無法被使用者選中的;
  4. 僞元素和僞類結合使用形如:

    .target:hover::after

JavaScript操作僞元素

 上文提到由于僞元素僅位于CSSOM中,是以我們僅能通過操作CSSOM API——

window.getComputedStyle

來讀取僞元素的樣式資訊,注意:我們能做的就是讀取,無法設定的哦!

{- window.getComputedStyle的類型 -}
data PseudoElement = ":before" | "::before" | ":after" | "::after" | ":first-line" | "::first-line" | ":first-letter" | "::first-letter" | "::selection" | ":backdrop" | "::backdrop" | Null

window.getComputedStyle :: HTMLElement -> PesudoElement -> CSSStyleDeclaration

{- CSSStyleDeclaration執行個體的方法 -}
data CSSPropertyName = "float" | "backround-color" | ......
data DOMPropertyName = "cssFloat" | "styleFloat" | "backgroundColor" | ......

-- IE9+的方法
CSSStyleDeclaration#getPropertyValue :: CSSPropertyName -> *
-- IE6~8的方法
CSSStyleDeclaration#getAttribute :: CSSPropertyName -> *
-- 鍵值對方式擷取
CSSStyleDeclaration#[DOMPropertyName] -> *
           

示例:

.target[title="hello world"]::after{
  display: inline-block;
  content: attr(title);
  background: red;
  text-decoration: underline;
}

const elTarget = document.querySelector(".target")
const computedStyle = window.getComputedStyle(elTarget, "::after")
const content = computedStyle.getPropertyValue("content")
const bg = computedStyle.getAttribute("backgroundColor")
const txtDecoration = computedStyle["text-decoration"]

console.log(content) // "hello world"
console.log(bg)      // red
console.log(txtDecoration) // underline
           

玩透Content屬性

 到這裡我們已經可以利用

::before

::after

實作tooltip等效果了,但其實更為強大的且更需花時間研究的才剛要開始呢!那就是Content屬性,不僅僅可以簡單直接地設定一個字元串作為僞元素的内容,它還具備一定限度的程式設計能力,就如上面

attr(title)

那樣,以其附屬元素的title特性作為content值。下面請允許我為大家介紹吧!

div::after{
    content: "普通字元串";
    content: attr(父元素的html屬性名稱);
    content: url(圖檔、音頻、視訊等資源的url);
    /* 使用unicode字元集,采用4位16進制編碼
     * 但不同的浏覽器顯示存在差異,而且移動端識别度更差
     */
    content: "\21e0";
    /* content的多個值可以任意組合,各部分通過空格分隔 */
    content: "'" attr(title) "'";
    
    /* 自增計數器,用于插入數字/字母/羅馬數字編号
     * counter-reset: [<identifier> <integer>?]+,必選,用于辨別自增計數器的作用範圍,<identifier>為自定義名稱,<integer>為起始編号預設為0。
     * counter-increment: [<identifier> <integer>?]+,用于辨別計數器與實際關聯的範圍,<identifier>為counter-reset中的自定義名稱,<integer>為步長預設為1。
     * <list-style-type>: disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha
     */
    content: counter(<identifier>, <list-style-type>);
    
    /* 以父附屬元素的qutoes值作為content的值
     */
    content: open-quote | close-quote | no-open-quote | no-close-quote;
}
           

換行符:HTML實體為

&#010

,CSS為

\A

,JS為

\uA

 可以看到Content接受6種類型,和一種組合方式。其中最後兩種比較複雜,我們後面逐一說明。

自定義計數器

 HTML為我們提供

ul

ol

li

來實作清單,但如果我們希望實作更為可性化的清單,那麼該如何處理呢?content屬性的counter類型值就能幫到我們。

<!-- HTML 部分-->
.dl
 .dt{chapter1}
 .dd{text11}
 .dd{text12}
 .dt{chapter2}
 .dd{text21}
 
/* CSS部分 */
.dl {
  counter-reset: dt 0; /* 表示解析到.dl時,重置dt計數器為0 */
  
  & .dt {
    counter-reset: dd 0; /* 表示解析到.dt時,重置dd計數器為0 */
    
    &::before{
        counter-increment: dt 1; /* 表示解析到.dt時,dt計數器自增1 */
        content: counter(dt, lower-roman) " ";
    }
  }
  
  & .dd::before {
    counter-increment: dd 1; /* 表示解析到.dd時,dd計數器自增1 */
    content: counter(dd) " ";
  }
}
           

通過

counter-reset

來定義和重置計數器,通過

counter-increment

來增加計數器的值,然後通過

counter

來決定使用哪個計數器,并指定使用哪種樣式。

 如果用JavaScript來表示應該是這樣的

const globalCounters = {"__temp":{}}

function resetCounter(name, value){
  globalCounters[name] = value
}
function incrementCounter(name, step){
  const oVal = globalCounters[name]
  if (oVal){
    globalCounters[name] = oVal + step
  }
  else{
    globalCounters.__temp[name] = step
  }
}
function counter(name, style){
    return globalCounters[name] || globalCounters.__temp[name]
}

function applyCSS(mount){
    const clz = mount.className
    if (clz == "dl"){
        resetCounter("dt", 0)
        const children = mount.children
        for (let i = 0; i < children.length; ++i){
          applyCSS(children[i])
        }
    }
    else if (clz == "dt"){
        resetCounter("dd", 0)
        incrementCounter("dt", 1)
        const elAsBefore = document.createElement("span")
        elAsBefore.textContent = counter("dt", "lower-roman") + " "
        mount.insertBefore(mount.firstChild)
    }
    else if (clz == "dd"){
        incrementCounter("dd", 1)
        const elAsBefore = document.createElement("span")
        elAsBefore.textContent = counter("dd", "lower-roman") + " "
        mount.insertBefore(mount.firstChild)
    }
}
           

嵌套計數器

 對于多層嵌套計數器我們可以使用

counters(<identifier>, <separator>, <list-style-type>?)

.ol
  .li
    .ol
      .li{a}
      .li{b}
  .li
    .ol
      .li{c}
           
.ol {
    counter-reset: ol;
    &amp; .li::before {
        counter-increment: ol;
        content: counters(ol, ".");
    }
}
           

Content的限制

  1. IE8+才支援Content屬性;
  2. 除了Opera9.5+中所有元素均支援外,其他浏覽器僅能用于

    :before,:after

    内使用;
  3. 無法通過JS擷取Counter和Counters的運算結果。得到的就隻能是

    "counter(mycouonter) \" \""

自定義引号

 引号這個平時很少在意的符号,其實在不同的文化中使用的引号将不盡相同,如簡體中文地區使用的

""

,而日本則使用

「」

。那我們根據需求自定義引号呢?答案是肯定的。

 通過

open-quote

,

close-quote

no-open-quote

no-close-quote

即可實作,下面我們通過例子來了解。

<q>

會根據父元素的

lang

屬性自動建立

::before

::after

來實作插入quotation marks。

p[lang=en]&gt;q{英語}
p[lang=no]&gt;q{挪威語}
p[lang=zh]&gt;q{漢語}
p[lang=en]&gt;q.no-quote{英語2}
div[lang=no]&gt;.quote{挪威語2}
           

CSS片段:

p[lang=en] &gt; q{
  quotes: "&lt;!--" "--&gt;"; /* 定義引号 */
}
p[lang=en] &gt; q.no-quote::before{
  content: no-open-quote;
  /*或者 content: none;*/
}
div[lang=no] &gt; .quote {
  quotes: "&lt;&lt;-" "-&gt;&gt;";
}
div[lang=no] &gt; .quote::before {
  content: open-quote;
}
div[lang=no] &gt; .quote::after {
  content: close-quote;
}
           

示例

分割線

p.sep{or}
           
.sep {
  position: relative;
  text-align: center;
  
  &amp;::before,
  &amp;::after {
    content: "";
    box-sizing: border-box;
    height: 1px;
    width: 50%;
    border-left: 3em solid transparent;
    border-right: 3em solid transparent;
    position: absolute;
    top: 50%;
  }
  
  &amp;::before {
    left: 0;
  }
  
  &amp;::after {
    right: 0;
  }
}
           

隻讀效果(通過遮罩原來的元素實作)

.input-group {
  position: relative;
  
  &amp;.readonly::before {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
  }
}
           

計數器

.selections&gt;input[type=checkbox]{option1}+input[type=checkbox]{option2}
.selection-count
           
.selections{
  counter-reset: selection-count;
  
  &amp; input:checked {
    counter-increment: selection-count;
  }
}
.selection-count::before {
  content: counter(selection-count);
}
           

最後

 尊重原創,轉載請注明來自:

https://www.cnblogs.com/fsjoh...

肥仔John^_^

參考

http://www.wozhuye.com/compat... https://dev.opera.com/article...

繼續閱讀