不用js,CSS 也能自动补全字符串

目录
文章目录隐藏
  1. 一、flex-end 对齐
  2. 二、CSS 变量动态计算
  3. 三、定义计数器样式
  4. 四、计数器的扩展
  5. 五、总结一下

很多时候都会碰到字符串补全的需求,典型的例子就时间或者日期中的补零操作,例如:

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;
}

flex-end 对齐

我们需要在数字前用伪元素生成一个“0”:

span::before{
  content: '0'
}

在数字前用伪元素生成一个“0”

接下来,给元素设置一个固定宽度,这里由于是等宽字体,所以可以直接设置为2ch,注意这个ch单位,它表示字符0的宽度。

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

元素设置一个固定宽度

原理很简单,在 2 个字符宽度的空间里放置 3 个字符,以右对齐的方式,是不是就自动把最左边的 0 给挤出去了?然后超出隐藏就可以了。

超出隐藏 0

完整代码如下:

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));
}

效果如下:

CSS 变量动态计算

具体的逻辑就是

  • 当 --num等于 10 时,透明度的计算值就是 0,直接按照 0 来渲染;
  • 当 --num大于 10 时,透明度的计算值就是负数值,会按照 0 来渲染;
  • 当 --num小于 10 时,透明度的计算值就是大于等于 1 的值,会按照 1 来渲染。

所以,最终的表现就是当大于等于 10 时不可见,小于 10 的时候可见。

但是,这样还是有点问题的,透明度不会影响元素的位置,如下:

透明度不会影响元素的位置

如何消除这个位置呢?方法有很多,这里采用 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

这样就比较完美了。

透明度不会影响元素的位置问题

完整代码如下:

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

三、定义计数器样式

利用计数器也能实现这一效果,首先看默认的计数器效果,我们需要隐藏原有的文字,利用计数器让 CSS 变量通过伪元素展示出来,如下:

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

flex-end 对齐

接下来需要用到 counter的第 2 个参数 <counter-style>,计数器样式。这是干什么的呢?相信大家都用过一个属性 list-style-type,就是和这个相通的,可以定义序列的样式,比如按照小写英文字母的顺序:

list-style-type: lower-latin;

按照小写英文字母的顺序

这里我们需要一个 10 以内自动补零的计数器,刚好有个现成的,叫做 decimal-leading-zero,翻译过来就是,十进制前置零。

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

十进制前置零

回到这里,需要做的就很简单了,补上这个参数就行了,完整代码如下:

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

效果如下:

效果如下

四、计数器的扩展

上面的计数器只适用于 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,这里简单介绍一下用法,假设定义一个计数器叫做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);
}

效果如下:

计数器的扩展

当然,这个兼容性略差,根据实际需求即可。

以上完整代码,可以访问:codepen

CSS 也能自动补全字符串

五、总结一下

以上介绍了 3 种 CSS 字符串补全方法,是不是又学到了几个小技巧呢?这几个方法各有千秋,比较一下各自优缺点:

  1. 第一种方案非常容易理解,也容易扩展,如果需要补全 3 位,只需要改变整体宽度即可,不足之处在于依赖等宽字体。
  2. 第二种方案比较符合 JS 逻辑,比较灵活,不足在于计算比较啰嗦,而且还要考虑 CSS 取值的容错性。
  3. 第三种方案是我比较推荐的了,无需计算,也不依赖布局,可能知道的同学不多,而且如果要自定义计数器,兼容性有点差。

关于 CSS 实现的优点,有很多,比如更容易维护、几乎不会报错、代码更加简洁等等,如果你学会了,赶紧在项目中用起来吧。

来源公众号:前端侦探

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » 不用js,CSS 也能自动补全字符串

发表回复