记一次Demo中遇到的防抖的问题

1,049 阅读3分钟

之前在写Pick Color色卡这个案例的时候,考虑到的一个多次点击 copy 键的问题,遂查阅资料后实现了节流的思想,在此做一次记录。

知识补充

1. 防抖是什么?

防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。

例如对于一个按钮,可以规定它在点击0.5s后再响应,若这0.5s内又被点击了,则重新计时,只要在0.5s内不再被点击就可以作出响应。

与防抖相关还有一个概念,那就是节流。

2. 节流是什么?

节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内这个事件被触发多次,只有一次能生效。

例如对于一个按钮,规定好它被多次点击的时候在一个单位时间内只能有一次响应。

3. 怎么实现函数防抖或者节流呢?

欸嘿,前面都提到了时间的问题,那是不是可以通过 setTimeout() 这个方法去实现呢?

是的╰(°▽°)╯

(1)手撕防抖函数

实现思想: 设置一个计时器,如果再次触发,我们会清空之前的定时器,重新生成一个定时器。

<button id="debounce">防抖(o゚v゚)</button>

<script>
  window.onload = function() {
    var myDebounce = document.getElementById("debounce");
    myDebounce.addEventListener("click", debounce(sayDebounce));
  }

  // 防抖函数,fn参数为接着要处理调用的函数
  function debounce(fn) {
    // 创建定时器
    let timeout = null;
    return function() {
      // 每次当用户再次点击的时候,用clearTimeout()把前一个定时器setTimeout()清除
      clearTimeout(timeout);
      // 然后创建一个新的 setTimeout,
      // 如果用户还点击了的话,就不会执行 fn 函数
      // 再点击就是重新计时
      timeout = setTimeout(() => {
        fn.call(this, arguments);
      }, 1000);
    };
  }

  function sayDebounce() {
    // ... 有些需要防抖的工作
    console.log("防抖成功(o゚v゚)/");
  }

</script>
(2)手撕节流函数

实现思想: 通过判断是否到达单位时间来触发函数。

<button id="throttle">节流(o゚v゚)</button>

<script>
  window.onload = function() {
    var myThrottle = document.getElementById("throttle");
    myThrottle.addEventListener("click", throttle(sayThrottle));
  }

  // 节流函数
  function throttle(fn) {
    // 通过闭包保存一个标记是否可以执行
    var canRun = true;
    return function() {
      // 如果标记为 true则可以执行
      if(!canRun) {
        return;
      }
      // 将标记设置为 false,防止执行之前再被执行
      canRun = false;
      // 定时器
      setTimeout( () => {
        fn.call(this, arguments);
        // 执行之后,重新将标志设置为 true
        canRun = true;
      }, 2000);
    };
  }

  function sayThrottle() {
    console.log("节流成功!(o゚v゚)/");
  }
</script>

我的实际应用

Pick Color 里呢,有三个对应的 HEX / RGB / HSL 的按钮,我想实现的是用户点击按钮后,可以获得点击的对应的文本存储在剪切板中,如何这个展示栏会展示两秒的“copied!”字样。

->

所以我要解决的问题有两个:一是希望将用户点击0.5s再作出响应,防止多次点击多次响应;二是在“copied”字样的2s里让这个div不被点击,不做响应。

因而学习了防抖之后,我就设置了0.5s的时间间隔,希望避免用户的多次点击:

var colorHsl = document.getElementsByClassName("copy-hsl")[0];

colorHsl.addEventListener("click", debounce(sayDebounce))

function debounce(fn) {
  let timeout = null;
  return function() {
    clearTimeout(timeout); 
    timeout = setTimeout(() => {
      fn.call(this, arguments);
    }, 500);
  };
}

function sayDebounce(){
  var pickColor = this.innerHTML;
  copyToClipboard(pickColor);
  this.innerHTML = "copied!"
  var time = 0;
  setTimeout(() => {
    this.innerHTML = pickColor;
  },2000);
}

第二个问题,我选择在“copied”字样出现之后给div设置一个class属性,将其pointer-events设置为null,也就是元素永远不会成为鼠标事件的target,也就不会触发click事件辽~

.no-click{
    pointer-events: none;
}

参考链接

掘金 | jsliang | 2019 面试准备 - JS 防抖与节流

最后

节流和防抖是很常见的一个性能优化的点,我也在项目过程中学习到了这个知识点,在这篇文章作纪录,若是本文中有什么错误的地方,希望掘友们可以指正~♪(´▽`)

另附 Pick Color色卡项目的源代码: Pick Color源代码

本文使用 mdnice 排版