js table 生成序号_CSS Counter 以及 CSS content 内容生成技术的实用价值

写这篇文章是因为,昨天在组里临时做了个分享,觉得还蛮有意思的,所以想记录下来。分享内容是讲 CSS Counter 的,从怎么使用它,到它的实际使用价值,以及一些延伸的用法。那我们直接进入正题。

怎么使用 CSS Counter

首先要明确的是,CSS Counter 从一开始就是为了解决列表项的序号展示需求而设计的。功能上,它能够用来统计元素的个数,并通过赋给 CSS 的 content 属性来展示在页面上。

那么,如果让你设计一个计数器,你会怎么设计呢?我觉得应该至少包含这几个部分: 1. 计数目标:你要统计什么 2. 计数增量:目标没出现一次,加多少 3. 清零:重置计数值 4. 计数值:当前出现的目标数量 * 计数增量

所以用 JS 代码可以这样写:

class Counter {
    count = 0 // 计数值

    constructor(target, increment) {
        // 计数算法
        if (appear(target)) {
            count += increment
        }
    }

    // 清零
    reset() {
        this.count = 0
    }
}

换成 CSS 的话,我们首先需要创建一个 Counter:

.reset {
    counter-reset: test;
}

“test” 表示计数器的标识。有人会疑问counter-reset不是代表重置的意思吗?对,它确实是重置计数器值的意思,但同时也起了创建一个计数器的作用。所以这行代码的完整意思是:创建一个计数器,它的标识是 “test”,并且在遇到 .reset 元素时,重置计数值。

接下来需要指定计数对象和每次计数的增量。同样的,CSS 在设计上将这两个功能合并成了一个属性。因为是为了统计“元素”的个数,所以在 CSS 中,计数目标是代表某个元素的选择器,比如:

.target {}

然后可以通过设置 counter-increment 属性来表示,计数器 test 每遇到一个 .target 元素就加1:

.target {
    // 第二个参数可以省略,默认为 1。
    counter-increment: test 1;
}

在 js 中,我们通过 counter.count 来获得计数值。而在 css 中,我们可以通过 counter() 方法来获取,并赋给 content 属性就可以在节点中展示出来了:

.count:before {
    content: counter(test);
}

另外还可以通过 counters 方法,获得嵌套的计数器的计数拼接值。这里的嵌套关系来源于元素的父子关系,如果两个设置了计数器的元素是父子关系,那么这两个计数器就是嵌套关系。代码示例:

.count:before {
    // 嵌套值会展示成这样的格式 "1-2-3"
    content: counters(test, "-");
}

更多 CSS Counter 的语法和使用方式,可以参考MDN 文档,这里就不再赘述。

CSS Counter 的实用价值

平时如果遇到有序列表的需求,序号都是通过模板硬编码实现的,比如:

<h2>《三十六计》</h2>
  <ol className="category">
{chapters.map((chapter, ci) => (
  <>
    <li className="title">
      {String.fromCharCode(97 + ci)}. {chapter.title}
      {chapter.sections && (
        <ol className="section">
          {(chapter.sections || []).map((section, si) => (
            <li className="title">
              {String.fromCharCode(97 + ci)}-
              {String.fromCharCode(97 + si)}. {section.title}
            </li>
          ))}
        </ol>
      )}
    </li>
  </>
))}
  </ol>

效果:

dd73b315ab84b84a24707adbf7db309b.png

这样就可以渲染出一个有序列表。对于类似的列表展示需求,大多数情况我不会想到使用 CSS 来实现,这里主要原因可能是语言上下文的切换成本 > 直接硬编码的成本。但是如果对于一些特殊需求,硬编码的成本 > 语言上下文切换的成本,我们就可以尝试使用 CSS Counter 来实现了。比如产品要求需要不再是“a, b, c, d…”而是 “壹,贰,叁,厮...”,这时候我们可能需要实现一个中文计数序号编码算法。可能有些同学说实现一个也不是很难啊?是的,但这取决于开发的知识储备,如果之前没有实现过的,成本肯定是大于直接使用 CSS 来实现的。使用 CSS,我们只需要设置一下counter 或 counters 的最后一个参数即可:

counter(test, 'simp-chinese-formal')

另外 CSS 还实现了非常多其他的序号样式,可以参考:list-style-type - CSS(层叠样式表) | MDN 比起实现计数序号编码算法,CSS 只用加个参数,显然要简单很多。 浏览器兼容性可参考:list-style-type - CSS(层叠样式表) | MDN 可以看到,很多都是可以在大多数生产环境中被使用了。所以,如果产品需求中出现了多种编码方式的序号,就可以考虑使用 CSS Counter 来实现。

下面再来看下另外一个功能需求 — 内容防复制。

80a3272d82a41ccfbc557efe641fb926.png

如上图所示,我们在选择列表的时候,可以发现,序号是没有被选上的。并且也无法被复制。之所以有这样的表现,是因为序号的内容是通过 css 的 content 属性来生成的,实际的 DOM 节点的 content 属性中,并没有内容。那么,我们是不是可以通过相同的技术来实现内容防复制功能呢?本质上,我们是利用了 CSS content 属性带来的特性。比如这样写,我们就可以将章节内容“隐藏”起来:

<h2>《三十六计》</h2>
  <ol className="category category--css">
{chapters.map((chapter, ci) => (
  <>
    <li className="title" data-content={chapter.title}>
      {chapter.sections && (
        <ol className="section">
          {(chapter.sections || []).map((section, si) => (
            <li className="title" data-content={section.title}>
            </li>
          ))}
        </ol>
      )}
    </li>
  </>
))}
  </ol>

.category--css .title:before {
  content: counters(test, "-", simp-chinese-formal) ". " attr(data-content);
}

我们再回到内容防复制功能本身,要实现这个功能并不是只有这一种方式,比如通过 -webkit-user-select: none;。虽然这在大多数环境中是有效的,但是在safari 中会遇到一个问题 -- 虽然看起来没被选中,但是确可以被复制。当你选择的区域包含可复制和不可复制的内容,那么在 safari 中,不可复制的区域就会被隐形选中,复制的内容中也会包含不可复制的内容,如图:

fc48a78ff5a9be2344bfa6f9ecb2e12c.png

所以我们必须将整个页面设置为 -webkit-user-select: none 才可以使得内容不可被复制。相比起来,使用 content 来实现不可复制,是一个兼容性更强的方案。

其实 content 内容生成技术,已经被使用在了很多场景里,比如使用 attr 方法展示图片 hover 提示、字体图标等。网上可以搜到很多资料,这里也不再展开。

总结

最后总结一下,如果产品需求里要求展示多种编码方式的序号,可以考虑使用 CSS Counter 来生成序号;如果想要实现文本不可复制功能,CSS content 生成技术是一个兼容性相对较好的方案。


附完整代码示例:

cranky-chaplygin-fltup - CodeSandbox​codesandbox.io
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值