jquery在一个元素后追加标签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="cdn.bootcss/jquery/1.8.3/jquery.js"></script>
<script>
var count=1;
var example = {
getData:function () {
var data ={
content:'df'+count++,
href:''
};
},
renderData:function (data) {
alert('事故发⽣点');
})
/*  setInterval(function () {
console.log('fdfdfg');
},2000);*/
/*⽤冒泡来绑定事件,类似于Jquery的on绑定事件*/
/* document.querySelector('body').addEventListener('click',function (e) {
if(e.ains('read-more')){
alert('事故发⽣点');
}
})*/
}
} ;
document.querySelector('.add_but').addEventListener('click',function (e) {
e.stopImmediatePropagation();
});
</script>
</body>
</html>
以上是我为说清这个事情写的⼀段测试代码,可以拷贝下来试试。当我们点击页⾯的按钮,触发调⽤e
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.ains('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: ''
};
},
renderData: function (data) {
alert('事故发⽣点'+t);
});
flag = true;
}
};
从逻辑上,看起来没有问题,但仔细观察,发现这是有问题的。当我们第⼆次,第三次刷新时,弹出框的内容还是和第⼀次模拟刷新后点击后弹出的内容⼀致,还是'事故发⽣点df1',⽽⾮和内容⼀样递增,为什么呢,感觉事件队列⾥⾯的回调函数被单独保存起来了,data被深拷贝了,⽽不再是⼀个引⽤。确实有点难理解,我也不知道到底是为什么,如果哪位能说清楚,还请⼀定告知。
结个尾写在最后,其实平常写⼀些程序时,事件绑定,造成程序重复执⾏这些情况很少发⽣,其通常会出现在我们写插件的时候,插件需要适应多种调⽤环境,所以在插件内部做到防⽌事件重复绑定的情况⾮常重要。
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,如果有疑问⼤家可以留⾔交流,谢谢⼤家对的⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。