CSS 也能自動補全字串?

XboxYan發表於2022-03-21
歡迎關注我的公眾號:前端偵探

很多時候都會碰到字串補全的需求,典型的例子就時間或者日期中的補零操作,例如

2021-12-31
2022-03-03

通常的做法是

if (num < 10) {
  num = '0' + num
}

後來,JS 中出現了原生的補全方法padStart()padEnd(),如下

'3'.padStart(2, '0')
// 結果是 ’03‘
'12'.padStart(2, '0')
// 結果是 ’12‘

其實呢,在 CSS 中也是可以實現這樣的效果的,並且有多種方案,下面一起看看吧,相信能有不一樣的體會

一、flex-end 對齊

先介紹一個比較容易理解的方案,也非常簡單,假設 HTML 是這樣的

<span>2</span>
-
<span>28</span>

一般情況下,還會設定等寬字型,看起來更加協調、美觀

span{
  font-family: Consolas, Monaco, monospace;
}

image.png

我們需要在數字前用偽元素生成一個“0”

span::before{
  content: '0'
}

image.png

接下來,給元素設定一個固定寬度,這裡由於是等寬字型,所以可以直接設定為2ch,注意這個ch單位,它表示字元0的寬度(有興趣的可以參考這篇文章:等寬字型在web佈局中應用以及CSS3 ch單位嘿嘿),然後設定右對齊就行了

span{
  /**/
  display: inline-flex;
  width: 2ch;
  justify-content: flex-end;
}

image.png

原理很簡單,在 2 個字元寬度的空間裡放置 3 個字元,以右對齊的方式,是不是就自動把最左邊的 0 給擠出去了?然後超出隱藏就可以了

image.png

完整程式碼如下

span::before{
  content: '0'
}
span{
  display: inline-flex;
  width: 2ch;
  justify-content: flex-end;
  overflow: hidden;
}

二、CSS 變數動態計算

由於 CSS 無法獲取標籤的文字內容,所以這裡需要構建一個 CSS 變數傳遞下去,如下

<span style="--num:2">2</span>
-
<span style="--num:12">28</span>

通過 var(--num)拿到變數以後,就可以進行一系列的邏輯判斷了,那麼,如何在小於 10 的情況下自動補零呢?

同樣我們需要在數字前用偽元素生成一個“0”

span::before{
  content: '0'
}

然後,只需要根據 CSS 變數動態隱藏這個偽元素就行了,先設定透明度,如下

span::before{
  /**/
  opacity: calc(10 - var(--num));
}

效果如下

image.png

具體的邏輯就是

  • --num等於 10 時,透明度的計算值就是 0,直接按照 0 來渲染
  • --num大於 10 時,透明度的計算值就是負數值,會按照 0 來渲染
  • --num小於 10 時,透明度的計算值就是大於等於1的值,會按照 1 來渲染

所以,最終的表現就是當大於等於10時不可見,小於10的時候可見

但是,這樣還是有點問題的,透明度不會影響元素的位置,如下

image.png

如何消除這個位置呢?方法有很多,這裡採用 margin-left 的方式,如下

span::before{
  /**/
  margin-left: clamp(-1ch, calc((9 - var(--num)) * 1ch),0ch);
}

這裡用到了clamp,你可以理解為一個區間,有 3 個值 [Min, Val, Max],前後分別是最小、最大值,中間是可變值(注意這裡是和 9 比較),所以這裡的邏輯就是

  • --num大於等於 10 時,假設為 15,中間 calc 值計算為 -5ch,clamp 取值為最小值 -1ch
  • --num小於 10 時,假設為 5,中間 calc 值計算為 5ch,clamp 取值為最大值 0ch

所以,最終的表現就是當大於等於10時margin-left為-1ch,小於10的時候margin-left為0

這樣就比較完美了

image.png

完整程式碼如下

span::before{
  content: '0';
  opacity: calc(10 - var(--num));
  margin-left: clamp(-1ch, calc((9 - var(--num)) * 1ch),0ch);
}

三、定義計數器樣式

利用計數器也能實現這一效果,首先看預設的計數器效果,我們需要隱藏原有的文字,利用計數器讓 CSS 變數通過偽元素展示出來,關於這個技巧,可以參考這篇文章:小tips: 如何藉助content屬性顯示CSS var變數值,如下

span::before{
  counter-reset: num var(--num);
  content: counter(num);
}

image.png

接下來需要用到 counter的第 2 個引數 <counter-style>,計數器樣式。這是幹什麼的呢?相信大家都用過一個屬性 list-style-type,就是和這個相通的,可以定義序列的樣式,比如按照小寫英文字母的順序

list-style-type: lower-latin;

image.png

這裡我們需要一個 10 以內自動補零的計數器,剛好有個現成的,叫做 decimal-leading-zero,翻譯過來就是,十進位制前置零

list-style-type: decimal-leading-zero;

image.png

回到這裡,需要做的就很簡單了,補上這個引數就行了,完整程式碼如下

span::before{
  counter-reset: num var(--num);
  content: counter(num, decimal-leading-zero);
}

效果如下

Kapture 2022-03-02 at 20.37.05.gif

四、計數器的擴充套件

上面的計數器只適用於2位數的,如果需要 3 位數的怎麼辦呢? 例如

001、002、...、010、012、...、098、099、100

JS 中的 padStart 可以指定填充後的位數

'1'.padStart(3, '0')
// 結果是 ’001‘
'99'.padStart(3, '0')
// 結果是 ’099‘
'101'.padStart(3, '0')
// 結果是 ’101‘

其實,CSS 中也是有這樣的能力的,叫做@counter-style/pad,嚴格來說,這才是官方的補全方案,語法也非常類似

pad: 3 "0";

但是,這個需要用在自定義計數器上,也就是@counter-style,有興趣的可以參考張老師的這篇文章:CSS @counter-style規則詳細介紹,這裡簡單介紹一下用法,假設定義一個計數器叫做pad-num,實現如下

@counter-style pad-num {
    system: extends numeric;
    pad: 3 "0";
}

語法是這樣的:這裡的system表示“系統”,就是內建的一些計數器,比如這裡用到了extends numeric,後面的numeric表示數字技術系統,前面的extends表示擴充套件,以這個為基礎,然後pad: 3 "0"就和 JS 的意義一樣了,表示不足 3 位的地方補“0”

然後運用到計數器中:

span::before{
  counter-reset: num var(--num);
  content: counter(num, pad-num);
}

效果如下:

Kapture 2022-03-03 at 15.57.33

當然,這個相容性略差,根據實際需求即可

以上完整程式碼可以訪問 CSS pad(codepen.io)

image-20220303163644336

五、總結一下

以上介紹了3種 CSS 字串補全方法,是不是又學到了幾個小技巧呢?這幾個方法各有千秋,比較一下各自優缺點:

  1. 第一種方案非常容易理解,也容易擴充套件,如果需要補全 3 位,只需要改變整體寬度即可,不足之處在於依賴等寬字型。
  2. 第二種方案比較符合 JS 邏輯,比較靈活,不足在於計算比較囉嗦,而且還要考慮 CSS 取值的容錯性。
  3. 第三種方案是我比較推薦的了,無需計算,也不依賴佈局,可能知道的同學不多,而且如果要自定義計數器,相容性有點差。

關於 CSS 實現的優點,有很多,比如更容易維護、幾乎不會報錯、程式碼更加簡潔等等,如果你學會了,趕緊在專案中用起來吧。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

歡迎關注我的公眾號:前端偵探

相關文章