欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

由JS for 循环中为元素添加点击事件到JS 中的事件委托

程序员文章站 2022-06-16 10:32:03
...

转: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

相关标签: 事件委托