由JS for 循环中为元素添加点击事件到JS 中的事件委托
转:https://blog.csdn.net/u014182411/article/details/74452536/
一 、问题的出现
在web中,我们常常需要为某一类型的元素添加事件,这时,常用for循环。对于刚刚接触不久的小白总是容易在这里犯错,比如我。因此今天在这里记录一下自己的踩过的坑,希望对大家有所帮助。
假如我们需要为页面上的一组按钮添加点击事件。采用for循环,最容易写成如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<button class="btn">btn0</button>
<button class="btn">btn1</button>
<button class="btn">btn2</button>
<button class="btn">btn3</button>
<button class="btn">btn4</button>
<button class="btn">btn5</button>
</body>
<script src="//code.jquery.com/jquery-1.12.4.js"></script>
<script>
var btnArray = document.getElementsByClassName('btn');
for (var i = 0; i < btnArray.length; i++) {
btnArray[i].onclick = function () {
alert(i);
};
}
</script>
</html>
此时点击每个按钮,并不是显示对应的按钮上的数字0、1、2……而是点击任何一个按钮时,显示的都是6。
二、一般解决方案
这个是为什么呢?
我们都知道JS中,内层函数是可以访问外层函数中的值的,并且可以直接调用。但是JS的函数是调用时触发。在每次的循环中onclick的函数体并没有执行。当点击按钮时,触发了onclick函数,这时再执行函数体中的内容。但是,此时循环已经结束。i的值已经在最后依次循环中变为了5+1=6。所以,所有按钮的点击事件触发时,i的值都是6。
那这个时候能采用什么办法来解决这个问题呢?
其中比较简单的一种方法就是为将循环写在一个闭包中。JS代码如下:
var btnArray = document.getElementsByClassName('btn');
for (var i = 0; i < btnArray.length; i++) {
btnArray[i].onclick = (function close(j) {
return function () {
alert(j);
}
})(i);
}
其中close就是一个闭包函数,你也可以省去函数名,作为一个匿名函数。这个闭包函数在声明后就立刻执行,这样这个函数的作用域里面就保存了i的值。函数的返回结果是事件处理函数,其中的参数j是close里面保存的j值,也就是循环时传入的值。这样再点击按钮时,出现的效果就是我们预想的效果。
当然,这里还有其他利用外部函数来解决这个问题的办法。比如,在将事件绑定的函数写在一个外围函数中:
var btnArray = document.getElementsByClassName('btn');
for (var i = 0; i < btnArray.length; i++) {
attach(i,btnArray[i]);
}
function attach(ii,o) {
o.onclick = function () {
alert(ii);
}
}
attach函数可以写在for循环之内。
类似的更多解决方案可以参考下面这篇博文:
http://www.cnblogs.com/liaopr/p/3928802.html
三、事件委托解决问题
采用这种方式解决上述问题,需要理解什么是事件委托。
我们应该找到事件绑定就是将一个事件与一个dom元素绑定在一起,当对dom元素进行操作时,就会触发绑定的事件。而事件委托是将事件绑定到我们原本想要绑定的元素的父元素,委托父类元素来根据触发条件触发相应的事件。打个比方,就相当于我们生活中的取快递。事件绑定相当于我们的快递到时,要自己去快递小哥那里取。事件委托就是快递先到我们小区管理人员那里。管理人员根据快递的收件人来通知需要取快递的那个人去取快递。快递就相当于事件,我们是子元素,管理人员是父类元素。
为什么可以这样用呢?这时因为浏览器处理dom事件的过程为:一、事件捕获 二、事件目标阶段 三、事件起泡阶段 。
事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素的节点流去,直到到达事件真正发生的目标元素。这个过程中,事件相应的监听函数是不会被触发的。
事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件起泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。
可以看出在事件起泡阶段时,事件流是不断向目标元素之上的节点推动的。目标元素的父类元素和祖先元素的事件处理函数都会执行。所以,我们在需要绑定事件的元素的父类元素绑定事件,在元素触发事件时,父类元素的事件监听会被执行,再根据事件监听内的相应条件,对子元素进行操作。
所以,我们这之前的问题可以这么解决(父类元素选取的是document):
document.onclick = function(event){
//IE doesn't pass in the event object
event = event || window.event;
//IE uses srcElement as the target
var target = event.target || event.srcElement;
target.onclick = function () {
alert(target.innerHTML.substr(3));
}
};
这个是使用原生JS写。还可以使用JQuery提供的delegate函数:
delegate() 方法为指定的元素(属于被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数。
使用 delegate() 方法的事件处理程序适用于当前或未来的元素(比如由脚本创建的新元素)可参见W3CShool:
http://www.w3school.com.cn/jquery/event_delegate.asp
$(selector).delegate(childSelector,event,data,function)
selector: 需要添加事件的元素的父类元素(必填)
childSelector:需要添加事件的元素(必填)
even:事件类型(必填)
data:传递到函数的额外参数(选填)
function:事件发生时需要执行的函数。
代码如下:
$(document).delegate('button','click',function () {
alert($(this).html().substr(3)); //$(this)访问添加事件的元素
})
四、使用事件委托的好处
在需要频繁创建和删除dom元素或创建大量的dom元素时,并为这些动态生成的元素添加事件时,建议使用事件委托。这时因为,这时使用事件绑定将存在较大的事件和内存上的开销。此外,使用事件绑定时,不能为新添加的元素绑定相应的事件。即,我们在事件绑定的函数之后添加了新的元素,新元素上没有相应的事件。测试代码如下:
var btnArray = document.getElementsByClassName('btn');
for (var i = 0; i < btnArray.length; i++) {
btnArray[i].onclick = (function close(j) {
return function () {
alert(j);
}
})(i);
}
var btn = document.createElement('button');
$(btn).html('hi');
$(btn).insertAfter('button')
html中后台添加的‘hi'的按钮是没有绑定click的事件的。
但是如果使用事件委托,则新添加的元素也是绑定了click事件的。如下代码:
$(document).delegate('button','click',function () {
alert($(this).html()); //$(this)访问添加事件的元素
});
var btn = document.createElement('button');
$(btn).html('hi');
$(btn).insertAfter('button')
关于更多事件委托的介绍可以参考如下三篇博客(介绍更为详细):
http://www.diguage.com/archives/71.html
http://www.uml.org.cn/AJAX/201610905.asp
https://juejin.im/entry/57ea329e67f3560057ad41a6
上一篇: 唯一有损她名誉的污点
下一篇: js用法、显示数据