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 id = setInterval(function () {
 console.log('定时器循环事件绑定')
 },1000);

上面的代码,相信不少同盟,天天都会写到,看似简单的事件绑定,却经常能给我们带来意想不到的结果,特别是在这个SPA,应用AJAX页面局部刷新如此盛行的时代。

那什么是事件绑定,造成的程序重复执行呢?这个事情要说清除,好像不是那么简单,还是用一段测试代码来说明吧。你可以拷贝到本地,自己试试:

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<button class="add_but">点击</button>
<div id="point">fdfsdf
</div>
<script src="https://cdn.bootcss.com/jquery/1.8.3/jquery.js"></script>
<script>
 var count=1;
 var example = {
 getData:function () {
  var data ={
  content:'df'+count++,
  href:''
  };
  this.renderData(data);
 },
 renderData:function (data) {
  document.getElementById('point').innerHTML='<div>this is a '+data.content+'点此<a class="read-more" href="javasript:;" rel="external nofollow" rel="external nofollow" >查看更多</a></div>';
  $('#point').on('click','.read-more',function () {
  alert('事故发生点');
 })
/*  setInterval(function () {
  console.log('fdfdfg');
  },2000);*/
  /*用冒泡来绑定事件,类似于Jquery的on绑定事件*/
 /* document.querySelector('body').addEventListener('click',function (e) {
  if(e.target.classList.contains('read-more')){
   alert('事故发生点');
  }
  })*/

 }
 } ;
 document.querySelector('.add_but').addEventListener('click',function (e) {
 example.getData();
 e.stopImmediatePropagation();
 });
</script>
</body>
</html>

以上是我为说清这个事情写的一段测试代码,可以拷贝下来试试。当我们点击页面的按钮,触发调用example.getData()这个函数,模拟ajax获取数据成功后,就会根据局部刷新页面内元素类名为point的内容,同时会为加载这个内容中的read-more A标签绑定一个事件,就这样我们想要的效果出现啦,当元素第一次加载时,页面正常,‘事故发生点'弹出一次,当二次刷新触发后,你会发现其弹出了两次,当第三次时,你会发现,其弹三次,以此类推。。。。

OMG,这个程序到底怎么了,我明明每次事件绑定前,前面绑定的元素都删除了,为什么,被删除的尸体感觉还在动作,好吧,上面就是我第一次遇到这个情况发出的感叹。

最后是问身边的大神,才突然领悟,原来绑定一直都在,而这个绑定被保存在一个叫做事件队列的地方,他不在循环执行的主线程中,画了一张需要默契才能看懂的图,勉强看一看。

事件队列

还原真相

其实上面那一段代码是为了测试而特意写的代码,除了定时器外,其他两个点击事件换个正常的写法,重复执行的情况是不会出现的,正常的代码:

 // jquery 事件直接绑定的写法;
 $('#point .read-more').on('click',function () {
  alert('事故发生点');
 })
 // 原生JS 事件直接绑定的写法;
 document.querySelector('.read-more').addEventListener('click',function (e) {
  alert('事故发生点');
 })

看出差别了吗?其实就是不用冒泡来事件委托,而是直接给添加的元素绑定事件。所以Dom事件是讲道理的,动态添加的元素,再动态为此绑定事件,待元素被删除后,与其绑定的相应事件其实是会从事件绑定队列中删除的,而非如上面测试代码,给人的感觉是元素移除后,但其绑定的事件还在内存中。但请记住,这是个误会,上面测试的代码之所以给人这种错觉,是因为我们并没有为动态添加的元素绑定事件,而仅仅是用了事件委托的形式,实际上事件是绑定在#point元素上的,其一直存在,利用事件冒泡来让程序知道我们点击了动态添加的链接元素。测试中特意用原生js去重现了这次事件委托,jquery的on绑定事件其实原理基本相同。

document.querySelector('body').addEventListener('click',function (e) {
 if(e.target.classList.contains('read-more')){
  alert('事故发生点');
 }
})

解除bug的那些方法

定时器

这个是最易犯的错误,当然也是最易解的错误,因为设定定时器时,其会返回一个数值,这个数值应该是事件队列此定时器中的一个编号吧,类似于9527;步骤就是设定一个全局变量来保持这个返回值id,在每次设定定时器时,先通过id清除已经设定过的定时器

 clearInterval(intervalId); //粗暴的写法
 intervalId&&clearInterval(intervalId); //严谨的写法
 intervalId=setInterval(function () {
  console.log('fdfdfg');
  },2000); 

Dom事件

其实上面我们已经说过,最直接的办法就是不采用事件委托,而是采用直接绑定;如果确实要用事件委托来绑定事件,那就是解绑。在jquery中提供了unbind函数来解绑事件,不过在jquery 1.8版本以后,这个方法已经不推荐了,而是推荐off方法。比如上面的on事件委托的方式,要解绑,可采用语句$('#point').off('click','.read-more')

有缺陷的解决方案,添加flag

很好理解,第一次绑定后,flag置位,下一次在执行这个绑定时,程序就知道在这个节点上已经有了绑定,无需再添加,具体操作就是:

 var flag = false;
 var example = {
 getData: function () {
  var data = {
  content: 'df' + count++,
  href: ''
  };
  this.renderData(data);
 },
 renderData: function (data) {
  document.getElementById('point').innerHTML = '<div>this is a ' + data.content + '点此<a class="read-more" href="javasript:;" rel="external nofollow" rel="external nofollow" >查看更多</a></div>';
  !flag && $('#point').on('click', '.read-more', function () {
  alert('事故发生点'+data.content);
  });
  flag = true;
 }
 };

从逻辑上,看起来没有问题,但仔细观察,发现这是有问题的。当我们第二次,第三次刷新时,弹出框的内容还是和第一次模拟刷新后点击后弹出的内容一致,还是'事故发生点df1',而非和内容一样递增,为什么呢,感觉事件队列里面的回调函数被单独保存起来了,data被深拷贝了,而不再是一个引用。确实有点难理解,我也不知道到底是为什么,如果哪位能说清楚,还请一定告知。

结个尾写在最后,其实平常写一些程序时,事件绑定,造成程序重复执行这些情况很少发生,其通常会出现在我们写插件的时候,插件需要适应多种调用环境,所以在插件内部做到防止事件重复绑定的情况非常重要。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • js 动态给元素添加、移除事件的实现方法

    最近项目中要要到 js动态给给元素绑定事件,刚好之前没用到过这些,顺便学习一下,于是google了一下 事件,写了如下两个事件 一个添加事件 ,一个是移除事件 /addEventListener(),removeEventListener()用于处理指定和删除事件处理程序的操作 //作用域:事件处理程序会在其所属元素的作用域内运行 //addEventListener(event,function,capture/bubble);removeEventListener(event,functio

  • js实现动态创建的元素绑定事件

    新创建的元素用传统的办法无法绑定,需要用live方法. 例: $('.rule').live('mouseover', function () { $(this).addClass("cancelable"); 以上这篇js实现动态创建的元素绑定事件就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • Javascript动态绑定事件的简单实现代码

    下面是页面的dom结构 复制代码 代码如下: <ul id="test"> <li>One</li> <li>Two</li> <li>Three</li> <li>Four</li> </ul> 下面是javascript代码 复制代码 代码如下: //根据ID获取对象 function id(v) { return document.getElementById(

  • ASP之处理用Javascript动态添加的表单元素数据的代码

    上一篇文章讲到了动态添加表单元素,这次我们来讲讲怎么样来处理这些动态添加的表单元素传过来的数据! 怎么样动态添加表单元素,我这里就不细说了!不明白的看看上一篇文章http://www.jb51.net/html/200711/23/12856.htm下面是动态添加表单元素的页面代码! 复制代码 代码如下: <html >  <head>  <title>动态添加表单元素BlueShine</title>  </head>  <script 

  • JS中动态添加事件(绑定事件)的代码

    两种方式:直接给对象添加事件,节点添加事件 例如给一个id为tab1的添加onclick事件 第一种情况: 复制代码 代码如下: var t = document.getElementById("tab1"); t.onclick = function tst(){ alert(''); } 第二种情况 复制代码 代码如下: var tb = document.getElementById("tab1"); if(window.addEventListener){

  • 用js动态添加html元素,以及属性的简单实例

    用js动态添加html元素,以及属性的简单实例 function test(){ //创建节点 var lswt_2=document.createElement("div"); //设置节点id lswt_2.id='lswtColse'; //设置节点属性 lswt_2.style.width='11px'; lswt_2.style.height='10px'; lswt_2.style.top='0px'; lswt_2.style.right='0px'; lswt_2.st

  • JS动态添加元素及绑定事件造成程序重复执行解决

    前言 本文主要给大家分享一下前段时间遇到的bug,这个Bug是关于jquery 的on方法绑交互事件,类似于$('#point').on('click','.read-more',function () {})这样的代码造成的程序重复执行,很多人在文章里写到了,也说了用off方法来解绑,但都未能点出问题的本质,几乎都忽略了问题的本质其实是事件委托造成的. 话不多说,上点天天看到的代码: 第一种:   $(document).on('click', function (e) { consol.lo

  • 关于动态生成dom绑定事件失效的原因及解决方法

    之前做项目都是直接用jquery的bind绑定事件,不过当时都不是动态生成dom元素,而是已经页面中原本存在的dom元素进行事件绑定,最近在测试给动态生成的dom绑定事件的时候发现事件失效,于是就测试了一下: 1. 事件失效的原因: (1)bind事件绑定只对dom中存在的元素有效,对于我们后来动态增加的元素是监测不到,所以绑定不了 (2)同样,当你使用var aa = document.getElementsByTagName("动态生成的元素");来获取动态生成的元素的时候也是获取

  • js 动态添加元素(div、li、img等)及设置属性的方法

    把一串 html 标签赋给一个 javascript 变量,除属性的值要用转义的双引号外,某些时候字符串还很长,显得有些复杂.如果用 js 动态添加元素,就不会有那么复杂的字符串出现,代码阅读性强一点,也容易理解. 网页是由 html 标签一层层组成的,js 也可以动态添加一层层的诸如 div.li.img 这样的标签.其实,不管是什么 html 标签,js 动态创建的方法都差不多,接着就先从动态添加 div 开始. 一.js 动态添加元素div <div id="parent"

  • JS动态给对象添加事件的简单方法

    WEB项目中,我们常常会碰到要动态对相应的对象添加事件,如下,有id="txtPrice"的文本框控件: <div> <input type="text" id="txtPrice" name = "txtPrice" value = "0"/> <div> 现在我们为其动态添加一事件,JS核心代码如下: document.getElementById("txt

  • js 动态生成html 触发事件传参字符转义的实例

    通常,在使用 JS 动态生成 html 的过程中,会嵌入相应的样式.事件等属性元素,而这时经常会出现所谓的 "单.双引号不够用" 的情况,别急,这时可以利用 html 语言中的转义字符来解决. 下面就来介绍一下相应的转义字符吧: & -- (ampersand) 转义字符对应为   & "   -- (double quote) 双引号,转义字符对应为  " '  -- (single quote)单引号,转义字符对应为  ' <  -- (l

  • js鼠标单击和双击事件冲突问题的快速解决方法

    情况一 如果在一个DOM对象上同时绑定单击(click)和双击(dblclick)事件,当在这个DOM对象上发生双击事件时,第一次点击(click)会 触发一次单击(click)事件,第二次点击(连续的)也会触发双击(dblclick)事件,还是会触发单击(click)事件(IE7与 firefox). 解决方法: <button onclick="test(1)" ondblclick="test(2)"></button> <scr

  • jQuery页面元素动态添加后绑定事件丢失方法,非 live

    代码1: 以此方法绑定的input框事件,在通过add按钮后用jquery绑定的事件 alert就会丢失 <input type="button" value="Add" name="test_but" /> <div id="test_div"><input name="test_input"/></div> <script> $('input[

  • js和jquery批量绑定事件传参数一(新猪猪原创)

    复制代码 代码如下: <input type="button" value="btn1" id="btn1"/> <input type="button" value="btn2" id="btn2"/> <input type="button" value="btn3" id="btn3"/>

  • jQuery动态生成的元素绑定事件操作实例分析

    本文实例讲述了jQuery动态生成的元素绑定事件操作.分享给大家供大家参考,具体如下: 这段时间在写代码的时候遇到一个问题,通过append()添加的节点,点击事件不起作用.当时我百思不得其解,各种找错都没找到错误的原因.后来才发现是动态生成节点的问题,还是自己没有经验啊. 下面来总结一下这个问题的始末,温故而知新. 先看下click绑定事件的效果. <div> <p>a</p> <p>b</p> <p>c</p> <

  • jquery mobile动态添加元素之后不能正确渲染解决方法说明

    jquerymobile动态添加元素之后有些不能被正确渲染的解决方法:listview: 添加 jq(".detail").listview("refresh");div或其他:添加.trigger( "create" );====================================================================== jqm在初始化页面时会根据data-xxx在各元素中插入jqm的属性和类等.在页面初

随机推荐