先看一个简单的例子吧(为了方便,例子使用jQuery写的):
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id='create'>点击生成新的button</button>
<div id="button_box"></div>
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script>
$(document).ready(function(){
//创建节点函数:
var createNew = function(){
var new_button=document.createElement("button");
new_button.innerHTML="点击触发事件";
//为节点添加一个类,以便通过这个类为节点绑定事件
$(new_button).addClass('event');
$("#button_box").append(new_button);
//为新创建的节点绑定事件
$('.event').on('click', function(){
console.log('clicked!');
})
};
//生成节点按钮的点击事件
$('#create').on('click', function(){
createNew();
});
});
</script>
</html>
实际效果如下:
点击按钮生成四个按钮节点——–>
现在尝试着点击第1个新节点,想想控制台会输出什么——–>
很明显,每一个click事件会触发一次控制台输出,但是现在我点击第1个事件,控制台输出了四次。
这就是我今天想要说的js的事件会重复绑定的问题。
一、为什么会重复绑定?
首先我们先来分析一下,为什么会重复绑定。
我们看看我们的绑定事件是什么时候?对,是在新节点创建的时候被绑定到节点上的,而且是通过jQuery的选择器通过类名event
将所有新节点都绑定同样的事件。因此,我们每点击一次按钮生成一个新节点都会为class=event
的节点绑定一次事件。而javascript是允许重复绑定事件的,所以问题就产生了。
当我创建了4次新节点时,第一个被创建的节点就被绑定了4次事件,所以产生了4次控制台的输出。
不要怀疑,为什么有这样的应用场景,要为这么多element绑定同样的事件。这种应用场景很普遍。比如一个类似于发帖子的页面,每一天回帖的样式和功能都是重复的,那么对于异步生成的新帖子绑定事件就会产生上面模拟的情况。
二、如何解决
通过$('.className').on('click', function(){ })
这种方法绑定的事件,势必会有重复的情况。
1、放弃通过选择器$('.className')
来绑定事件:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id='create'>点击生成新的button</button>
<div id="button_box"></div>
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script>
$(document).ready(function(){
//创建节点函数:
var createNew = function(){
var new_button=document.createElement("button");
new_button.innerHTML="点击触发事件";
//为节点添加一个类,以便通过这个类为节点绑定事件
$(new_button).addClass('event');
$("#button_box").append(new_button);
//为新创建的节点绑定事件
//直接将事件绑定到新节点,跳过类选择器的重复问题
$(new_button).on('click', function(){
console.log('clicked!');
})
};
//生成节点按钮的点击事件
$('#create').on('click', function(){
createNew();
});
});
</script>
</html>
这样每个element都唯一的绑定了一次事件,实际效果如下:
点击按钮生成四个按钮节点——–>
点击第1个新节点,控制台输出结果如下——–>
思考一下,这样做有一个不方便的地方:
如果我每次生成的新元素element有多个,我还能需要一个个地遍历然后单独为每一个添加。显然这样没有通过选择器$('.className')
绑定事件的优势更大。
2、通过选择器$('.className')
绑定事件前,先解绑:
解绑的方法很多,根据事件绑定的方法分别会有如下的对应关系:
- jQ的事件解除绑定分别是:
绑定方法 | 解绑方法 |
---|---|
bind | unbind |
on | off |
live | die |
delegate | undelegate |
对于上面的例子,在绑定之前清除一次click事件:
//先清除一次事件
$('.event').off('click');
//为新创建的节点绑定事件
$('.event').on('click', function(){
console.log('clicked!');
})
这样执行出来的结果就和上面的解决方法一样。
这样就可以解决上面提到的问题了。同样再来深入的思考一下,绑定之前清除一次click事件又会带来什么问题?
如果之前元素已经绑定了另外一个click事件,那么现在的解绑就会造成之前事件的解绑,这样也是不符合期望的。
三、总结
事件重复绑定会带来的问题我这里就不多说了,这篇文章也只介绍了两种解决方法,各有优缺点,可以按照实际的情况去选择合适的方法。如果后面有更多地解决方法我会继续更新,当然如果有更好的解决方法,欢迎指教。