还在用 JS 做节流吗?CSS 也可以防止按钮重复点击

开发 前端
CSS 实现“节流”其实就是控制一个动画的精准控制,假设有一个动画控制按钮从禁用->可点击的变化,每次点击时让这个动画重新执行一遍,在执行的过程中,一直处于禁用状态,这样就达到了“节流”的效果。

众所周知,函数节流(throttle)是 JS 中一个非常常见的优化手段,可以有效的避免函数过于频繁的执行。

举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库。

btn.addEventListener('click', _.throttle(save, 300))

其实除了 JS 方式, CSS 也可以非常轻易的实现这样一个功能,无需任何框架库,一起看看吧!

一、CSS 实现思路分析

CSS 实现和 JS 的思维不同,需要从另一个角度去看待这个问题。

比如这里的需要对点击事件进行限制,也就是禁用点击事件,想想有什么方式可以禁用事件,没错,就是pointer-events;

然后是时间的限制,每次点击后需要自动禁用300ms,时间过后重新恢复,那么,有什么特性和时间以及状态恢复有关呢?没错,就是animation;

除此之外,还需要有触发时机,这里是点击行为,所以必然和伪类:active有关联。

因此,综合分析,实现这样一个功能需要用到pointer-events、animation以及:active,那么如何将这些思路串联起来呢?

图片

思考3秒...

🤔

🤔

🤔

你想到了吗?💡💡💡

其实这种场景可以理解成是对 CSS 动画的控制,比如有一个动画控制按钮从禁用->可点击的变化,每次点击时让这个动画重新执行一遍,在执行的过程中,一直处于禁用状态,是不是就达到了“节流”的效果了?

接下来看看具体实现

二、CSS 动画的精准控制

假设有一个按钮,绑定了一个点击事件。

<button notallow="console.log('保存')">保存</button>

这时的按钮连续点击就会不断地触发,效果如下:

图片

下面定义一个关于pointer-events的动画,就叫做 throttle 吧!

@keyframes throttle {
from {
pointer-events: none;
}
to {
pointer-events: all;
}
}

很简单吧,就是从禁用到可点击的变化。

接下来,将这个动画绑定在按钮上,这里为了方便测试,将动画设置成了2s。

button{
animation: throttle 2s step-end forwards;
}

注意,这里动画的缓动函数设置成了阶梯曲线,step-end,它可以很方便的控制pointer-events的变化时间点。

有兴趣的可以参考这篇文章:CSS3 animation属性中的steps功能符深入介绍 « 张鑫旭-鑫空间-鑫生活 (zhangxinxu.com)[1]。

如下示意,pointer-events在0~2秒内的值都是none,一旦到达2秒,就立刻变成了all,由于是forwards,会一直保持all的状态。

图片

最后,在点击时重新执行一遍动画,只需要在按下时设置动画为none就行了。

这个技巧之前在这篇文章中有更详细的介绍:CSS 实现按钮点击动效的套路。

实现如下:

button:active{
animation: none;
}

为了演示方便,我们暂时把颜色变化也加在动画里。

@keyframes throttle {
from {
color: red;
pointer-events: none;
}
to {
color: green;
pointer-events: all;
}
}

现在如果文字是red,表示是禁用态,只有是green,才表示可以被点击,非常清晰明了,如下:

图片

下面是最终点击对比效果,很好地限制了点击频率。

图片

完整代码如下,就这么几行,如果需要改限制时间,直接改动画时间就行了。

button{
animation: throttle 2s step-end forwards;
}
button:active{
animation: none;
}
@keyframes throttle {
from {
pointer-events: none;
}
to {
pointer-events: all;
}
}

你也可以查看以下任意链接:

  • CSS throttle (codepen.io)[2]
  • CSS throttle - 码上掘金 (juejin.cn)[3]
  • CSS throttle (runjs.work)[4]

三、CSS 实现的其他思路

借用这种思路,也可以很轻松的实现节流的效果。而且为了更好的体验,可以用上真正的按钮禁用。

btn.disabled = true

具体思路是这样的,通过:active去触发transition变化,然后通过监听transition回调去动态设置按钮的禁用状态,实现如下:

定义一个无关紧要的过渡属性,比如opacity。

button{
opacity: .99;
transition: opacity 2s;
}
button:not(:disabled):active{
opacity: 1;
transition: 0s;
}

然后监听transition的起始回调。

// 过渡开始
document.addEventListener('transitionstart', function(ev){
ev.target.disabled = true
})
// 过渡结束
document.addEventListener('transitionend', function(ev){
ev.target.disabled = false
})

这样做的最大好处是,这部分禁用的逻辑是完全和业务逻辑是解耦的,可以在任意时候,任意场合下无缝接入,也不受框架和环境影响,效果如下

图片

完整代码也可以查看以下任意链接:

  • CSS throttle disabled (codepen.io)[5]
  • CSS throttle disabled - 码上掘金 (juejin.cn)[6]
  • CSS throttle disabled (runjs.work)[7]

四、总结一下

以上通过 CSS 的思路实现了类似“节流”的功能,相比 JS 实现而言,实现更精简、使用更简单,没有框架限制,下面一起总结一下实现要点:

  • 函数节流是一个非常常见的优化方式,可以有效避免函数过于频繁的执行。
  • CSS 的实现思路和 JS 不同,重点在于在于找到和该场景相关联的属性。
  • CSS 实现“节流”其实就是控制一个动画的精准控制,假设有一个动画控制按钮从禁用->可点击的变化,每次点击时让这个动画重新执行一遍,在执行的过程中,一直处于禁用状态,这样就达到了“节流”的效果。
  • 还可以通过 transition 的回调函数动态设置按钮禁用态。
  • 这种实现的好处在于禁用逻辑和业务逻辑是完全解耦的。

不过,这种实现方式还是比较有局限的,仅限于点击行为,像很多时候,节流可能会用在滚动事件或者键盘事件上,像这些场景就用传统方式实现就行了。

参考资料

[1] CSS3 animation属性中的steps功能符深入介绍 ? 张鑫旭-鑫空间-鑫生活 (zhangxinxu.com​): https://www.zhangxinxu.com/wordpress/2018/06/css3-animation-steps-step-start-end/

[2] CSS throttle (codepen.io): https://codepen.io/xboxyan/pen/rNKmmVq

[3] CSS throttle - 码上掘金 (juejin.cn): https://code.juejin.cn/pen/7164961819369570345

[4] CSS throttle (runjs.work): https://runjs.work/projects/47885939389440f4

[5] CSS throttle disabled (codepen.io): https://codepen.io/xboxyan/pen/oNyWwvB

[6] CSS throttle disabled - 码上掘金 (juejin.cn): https://code.juejin.cn/pen/7164994189032161311

[7] CSS throttle disabled (runjs.work): https://runjs.work/projects/41e8b998624743fc

责任编辑:武晓燕 来源: 前端侦探
相关推荐

2022-12-26 08:25:16

CSS函数节流

2024-08-15 08:56:17

2024-10-11 16:34:22

2012-07-19 10:03:32

2022-09-13 17:54:55

CSS定时器监听事件

2021-11-02 16:44:40

部署DevtoolsJRebel

2021-12-06 17:44:56

MHAMySQL高可用

2021-01-03 17:14:16

ORMObjective S运行

2020-03-04 14:05:35

戴尔

2014-09-12 17:55:42

2022-05-05 09:14:41

AlpineDocker镜像开发

2020-03-10 10:37:48

数据丢失数据泄露网络安全

2018-05-29 10:20:50

人工智能机器人计算机

2015-08-27 09:30:38

2011-05-04 13:28:07

喷墨打印机供墨

2020-02-11 11:37:37

比特币区块链加密

2020-05-14 18:05:55

OptionalJava非空判断

2024-09-27 07:49:06

数据库机械硬盘存储数据

2024-04-11 09:17:51

ArraysJava安全

2021-10-14 18:15:38

BeanUtils对象生成器
点赞
收藏

51CTO技术栈公众号