由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

猜你喜欢

转载自blog.csdn.net/hangGe0111/article/details/81458975
今日推荐