实现防抖节流 - 使用drag拖拽事件+input keydown事件模拟场景

3,576 阅读3分钟

    为了加深自己对防抖+节流的理解,实现两个简单的案例来加深自己的印象。

一、函数节流(throttle)

通过实现一个拖拽Dom元素小案例来加深对函数节流的理解

  • 情景:当drag拖拽事件执行,1秒内调用多次DragFn函数。
  • 实现效果:如果1秒内执行了多次函数,那么函数1秒只能执行一次(5s执行5次)。

  • drag.js - 示例code
"use strict";

// 函数节流(throttle) - 情景:当drag拖拽事件执行,1秒内调用多次DragFn函数。
// 实现效果:如果1秒内执行了多次DragFn函数,那么DragFn函数1秒只能执行一次(5s执行5次)。
(function(){
  const box_dom  = document.getElementsByClassName('box')[0];
  let isDrag = false; //拖拽状态
  let drag_position = {}; //记录开始拖拽时元素坐标
  
  // 开始拖拽
  // clientX/clientY - 鼠标指针的水平/垂直坐标
  box_dom.addEventListener('dragstart',function(e){
    if(e.target === box_dom){
      isDrag = true;
      //getBoundingClientRect方法返回元素的大小及其相对于视口的位置
      let {left,top} = box_dom.getBoundingClientRect(); 
      drag_position.left = e.clientX - left;
      drag_position.top = e.clientY - top;
    }
  
  },false)
  
  const DragFn = throttle(Fn,1000);
  // 拖拽移动
  box_dom.addEventListener('drag',DragFn,false);
  // 拖拽结束
  box_dom.addEventListener('dragend',function(e){
    isDrag = false;
  },false)
  
  function throttle(fn,timer){
    let init_time = 0;
    // 回调函数提供给drag事件
    return function(...args){
      let now_time = +new Date();
      if(now_time-init_time > timer){
        init_time = now_time;
        fn.apply(this,args);
      }
    }
  }
  // 节流执行的函数
  function Fn(e){
    e.preventDefault();
    if(isDrag&&e.clientX!==0){
      let t_left = e.clientX - drag_position.left;
      let t_top = e.clientY - drag_position.top;
      box_dom.style.left = t_left+'px';
      box_dom.style.top = t_top+'px';

      const timer = new Date();
      console.log(`${timer.getMinutes()}${timer.getSeconds()} ---------- Drag Dom----------`);
    }
  }
})()

二、函数防抖(debounce)

input如果2秒之内继续输入内容,那么计时器重新回到2秒,函数不执行(回表),等待输入完成2秒时间到后执行函数。

  • 作用:例如通过监听input keydown事件,请求搜索内容,为了避免过于频繁的执行请求,需要使用函数防抖来实现优化。

  • search.js - 示例code
"use strict";
// 函数防抖(debounce) - 情景:当input输入内容时,像后台请求搜索内容。
// 实现效果:如果2秒之内重新输入,那么计时器重新回到2秒函数不执行(回表),等待输入完成2秒时间到后执行函数。

(function(){
  const SeachDom = document.getElementsByTagName('input')[0];

  const KeyDownFn = debounce(Fn,2000);
  SeachDom.addEventListener('keydown',KeyDownFn);

  function debounce(fn,timer){
    let timer_out = null;

    return function(...args){
      // 清除计时器
      if(timer_out) clearTimeout(timer_out);

      timer_out = setTimeout(()=>{
        fn.apply(this,args);
      },timer)

    }
  }

  // 请求搜索内容操作
  function Fn(e){
    console.log(e.target.value, '<------search values');
  }

})();

三、HTML 代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>函数防抖/节流Demo</title>
  <style>
    html,body{
      width: 100%;
      height: 100%;
      padding:0;
      margin:0;
    }
    .box,.debounce{
      width: 200px;
      height: 200px;
      background: skyblue;
      position: absolute;
      left: 20px;
      top: 20px;
      border-radius: 10px;
    }
    .debounce{
      top: 300px;
      background: yellowgreen;
      line-height: 200px;
      text-align: center;
      padding: 0 20px;
    }
    
    .debounce input{
      top: 300px;
      display: inline-block;
      height: 30px;
      width: 100%;
      font-size: 16px;
      padding-left: 10px;
    }
    .box{
      cursor: move;

    }
    .box>p,.debounce>p{
      margin: 0;
      display: flex;
      height: 100%;

      line-height: 30px;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      color: #333;
    }
  </style>
</head>
<body>
  <section class="box" draggable="true">
    <p>
      <span>
        拖拽节流
      </span>
      <span>
          (1秒移动一次)
      </span>
    </p>
    

  </section>
  <section class="debounce">
    <p>
      <input type="text" placeholder="搜索防抖" class="search-input">
      <br/>
      <span>
        输入完成2秒后
      </span>
      <span>执行搜索请求</span>
    </p>
  </section>

  <!-- 拖拽 -->
  <script src='./src/drag.js'></script>
  <!-- 搜索 -->
  <script src='./src/search.js'></script>
</body>
</html>