zoukankan      html  css  js  c++  java
  • JS动态添加元素及绑定事件造成程序重复执行解决

    在这里记录下遇到的一个bug,这个Bug是关于jquery 的on方法绑定事件,类似于$('#point').on('click','.read-more',function () {})这样的代码造成的程序重复执行,很多人在文章里写到了,也说了用off方法来解绑,但都未能点出问题的本质,几乎都忽略了问题的本质其实是事件委托造成的。

    事件绑定

    我们平时用到的事件绑定基本是以下三种:

    • 第一种
    $(document).on('click', function (e) {
      consol.log('jquery事件绑定')
    });
    
    • 第二种
    document.addEventListener('click',function (e) {
      consol.log('原生事件绑定')  
    });
    
    • 第三种
    var timer = setInterval(function () {
      console.log('定时器循环事件绑定')
    },1000);
    

    那什么是事件绑定造成的程序重复执行呢?这个事情要说清楚,好像不是那么简单,还是用一段测试代码来说明吧:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        .mask {
          display: none;
        }
        .mask_con {
           80%;
          margin-left: 10%;
          height: 80px;
          border: 1px solid #ccc;
          display: flex;
          align-items: center;
          justify-content: space-around;
        }
      </style>
    </head>
    <body>
      <button class="buy_btn">购买</button>
      <button class="more_btn">更多</button>
      <div class="mask">
        <div class="mask_con"></div>
      </div>
    
      <script src="./js/jquery-2.2.4.min.js"></script>
      <script>
        $(document).on("click", ".closeMask", function (e) {
          e.preventDefault();
          $(".mask").hide();
        })
        $(document).on("click", ".buy_btn", function (e) {
          e.preventDefault();
          showMask("buy");
        })
        $(document).on("click", ".more_btn", function (e) {
          e.preventDefault();
          showMask("more");
        })
    
        function showMask(text) {
          if (text == "buy") {
            $(".mask_con").html(`
              <button class="btn">去购买</button>
              <button class="closeMask">关闭</button>
              `)
          } else if (text == "more") {
            $(".mask_con").html(`
              <button class="btn">查看更多</button>
              <button class="closeMask">关闭</button>
              `)
          }
          $(".mask").show();
          $(document).on("click", ".btn", function (e) {
            e.preventDefault();
            if (text == "buy") {
              alert("去购买");
            } else if (text == "more") {
              alert("查看更多")
            }
          })
        }
      </script>
    </body>
    </html>
    

    我们看下这段代码的效果:

    当我们点击页面上的按钮时,会出现弹窗,点击弹窗中的按钮,会alert不同的内容。但当我们多次点击出现弹窗后,alert也弹出了一次、两次、三次...
    明明是每次事件绑定前,mask_con元素中的内容都更新了,相当于之前绑定的元素都不在了,为什么像是被删除的元素依然有事件绑定。
    查阅后,突然反应过来,原来绑定一直都在,而这个绑定被保存在一个叫做事件队列的地方,他不在循环执行的主线程中,画了一张需要默契才能看懂的图,勉强看一看。

    如果我们不使用冒泡来事件委托,而是直接给添加的元素绑定事件,是不会有问题的。

    $(".btn").on("click",function(e) {
      e.preventDefault();
      if(text == "buy") {
        alert("去购买");
      }else if(text == "more") {
        alert("查看更多")
      }
    })
    

    所以Dom事件是讲道理的,动态添加的元素,再动态为此绑定事件,待元素被删除后,与其绑定的相应事件其实是会从事件绑定队列中删除的,而非如上面测试代码,给人的感觉是元素移除后,但其绑定的事件还在内存中。但请记住,这是个误会,上面测试的代码之所以给人这种错觉,是因为我们并没有为动态添加的元素绑定事件,而仅仅是用了事件委托的形式,实际上事件是绑定在document元素上的,其一直存在,利用事件冒泡来让程序知道我们点击了动态添加的按钮元素。

    解除绑定的方法

    • 定时器中的事件绑定:
      设定一个全局变量来保存这个定时器,在每次设定定时器时,先清除已经设定过的定时器。
    clearInterval(timer); //粗暴的写法
    timer&&clearInterval(timer); //严谨的写法
    timer=setInterval(function () {
      console.log('定时器');
    },2000); 
    
    • DOM中的事件绑定:
      其实上面我们已经说过,最直接的办法就是不采用事件委托,而是采用直接绑定;
      如果确实要用事件委托来绑定事件,那就是解绑。
      在jquery中提供了unbind函数来解绑事件,不过在jquery 1.8版本以后,这个方法已经不推荐了,而是推荐off方法。
      比如上面的on事件委托的方式,要解绑,可采用语句$(document).off('click','.btn') 。
  • 相关阅读:
    Luogu 4206 [NOI2005]聪聪与可可
    【Luogu】P3708Koishi的数字游戏(数论)
    【Luogu】P1850换教室(期望DP)
    【Luogu】P1231教辅的组成(拆点+Dinic+当前弧优化)
    【Luogu】P3865ST表模板(ST表)
    【Luogu】P3376网络最大流模板(Dinic)
    【Luogu】P1005矩阵取数游戏(高精度+DP)
    【Luogu】P2324骑士精神(IDA*)
    【Luogu】P3052摩天大楼里的奶牛(遗传算法乱搞)
    洛森地图半成品
  • 原文地址:https://www.cnblogs.com/ZerlinM/p/14061568.html
Copyright © 2011-2022 走看看