JavaScript(JQ)事件委托以及JS闭包小记

本文介绍了JavaScript的事件委托原理,通过冒泡机制管理事件处理,以减少内存消耗和提高性能。同时,讨论了闭包的概念,解释了在循环中使用闭包为元素添加事件时可能出现的问题及解决方案。此外,还提供了JS实现事件委托和闭包的实例代码。

一开始看的时候有些晕,可以监听DOM元素的方法太多,主要是写在<script></script>,下面列出来理清楚:

JS原生:

document.getElementById("menuultest").onmouseover = function(ev){    };

JQ:该段参考自https://codeplayer.vip/p/j7sq1

// jQuery 1.0+ (1.4.3+支持参数data)
$("selector").click( [ data ,] handler );

// jQuery 1.0+ (1.4.3+支持参数data)
$("selector").bind( "click" [, data ], handler );

// jQuery 1.3+ (1.4+支持参数data)
$("selector").live( "click" [, data ], handler );

// jQuery 1.4.2+
$("ancestor").delegate( "selector", "click" [, data ], handler );

// jQuery 1.7+
$("ancestor").on( "click", "selector" [, data ], handler );

注:从jQuery 1.7开始,on()函数提供了绑定事件处理程序所需的所有功能,用于统一取代以前的bind()、 delegate()、 live()等事件函数。

==============================结束引用===========================

什么是事件委托:

利用冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

事件委托和事件监听的区别:

在js中,常用到element.addEventListener()来进行事件的监听。但是当页面中存在大量需要绑定事件的元素时,这种方式可能会带来性能影响。此时,我们可以用事件委托的方式来进行事件的监听,所以可以说事件委托是一种变相的事件监听方式。

形象化事件委托:

想象公司有两个员工要收快递,有办法是两个人在公司门外等快递送来或者是快递员打电话到了再来拿,不考虑这两种方法,那我们是不是可以想象公司前台有人帮所有公司员工签收,也就相当于委托公司前台代收,然后员工再自己来取。

 接下来看看原生JS和JQ怎么实现事件委托,先给出一段html:

<input type="button" name="" id="btn" value="添加" />
  <ul id = "menuultest">
     <li>
       <img src="images/btnimg01.jpg"></img>
       <a href="#">第一条</a>
     </li>
    
      <li>
       <img src="images/btnimg01.jpg"></img>
       <a href="#">第二条</a>
     </li>
  </ul>

一、JS实现点击动态增加一行、鼠标经过效果有背景变化效果

var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("menuultest");
    var aLi = oUl.getElementsByTagName('li');
    var num = 3;
    //添加新节点
            oBtn.onclick = function(){
                var oLi = document.createElement('li');
                oLi.innerHTML = '<li><img src="images/btnimg01.jpg"></img><a href="#">'+(111*num++)+'</a></li>'
                oUl.appendChild(oLi);
            };

            oUl.onclick = function(ev){
              e = ev || window.event;
              var target = e.target || e.srcElement;
              console.log(target.nodeName);
             if(target.nodeName == "A"){
                 console.log("点击了a标签");

              }else if(target.nodeName == "IMG"){
                 console.log("点击了img标签");
              }     
            }
            
            //事件委托,添加的子元素也有事件
            oUl.onmouseover = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#FFEEFF";
                }
                
            };
            oUl.onmouseout = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#fff";
                }
                
            };

二、JQ实现点击动态增加一行、鼠标经过效果有背景变化效果

var num = 3;
    $("#btn").on('click',function(e){
       $("#menuultest").append('<li><img src="images/btnimg01.jpg"></img><a href="#">'+(111*num++)+'</a></li>')
    });

    $("#menuultest").on('click',function(e){
       e = e || window.event;
       var target = e.target || e.srcElement;
       console.log(target.nodeName);
       if(target.nodeName == "A"){
          console.log("点击了a标签");

       }else if(target.nodeName == "IMG"){
          console.log("点击了img标签");
       }
    });

$("#menuultest").on('mouseover',function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#FFEEFF";
                }
    });
    

    $("#menuultest").on('mouseout',function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#fff";
                }
    });

注:.bind的写法也类似,不过还是推荐使用.on方法

$('#menuultest').bind('click', function (e) {
       e = e || window.event;
       var target = e.target || e.srcElement;
       console.log(target.nodeName);
       if(target.nodeName == "A"){
          console.log("点击了a标签");

       }else if(target.nodeName == "IMG"){
          console.log("点击了img标签");
       }
     });

三、JS闭包特性如何给循环中的对象添加事件

 本段参考自https://www.imooc.com/article/13833

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引,举个例子:点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4
 <!DOCTYPE HTML>
  <html>
  <head>
  <meta charset="utf-8" />
 <title>闭包演示</title>
  <style type="text/css">
      p {background:gold;}
  </style>
  <script type="text/javascript">
function init() {
    var pAry = document.getElementsByTagName("p");  
   for( var i=0; i<pAry.length; i++ ) {
        pAry[i].onclick = function() {
         alert(i);
   }
  }
}
</script>
</head>
<body onload="init();">
<p>产品 0</p>
<p>产品 1</p>
<p>产品 2</p>
<p>产品 3</p>
<p>产品 4</p>
</body>
</html>

即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。

通过element.οnclick=function(){alert(i);}方式给元 素添加点击事件。响应函数function(){alert(i);}中的 i 并非每次循环时对应的 i(如0,1,2,3,4)而是循环后最后 i 的值5。 或者说循环时响应函数内并未能保存对应的值 i,而是最后一次i++的值5。
了解了原因,下面就由几种方式可与解决:

1、将变量 i 保存给在每个段落对象(p)上

<script> 
 function init1() {
    var pAry = document.getElementsByTagName("p");
    for( var i=0; i<pAry.length; i++ ) {
      pAry[i].i = i;
       pAry[i].onclick = function() {
          alert(this.i);
      }
    }
  }
</script>

2、加一层闭包,i 以函数参数形式传递给内层函数

  function init3() {
   var pAry = document.getElementsByTagName("p");
    for( var i=0; i<pAry.length; i++ ) {
     (function(arg){
         pAry[i].onclick = function() {
            alert(arg);
        };
     })(i);//调用时参数
    }
}

3、加一层闭包,i 以局部变量形式传递给内层函数

  function init4() {
   var pAry = document.getElementsByTagName("p");
    for( var i=0; i<pAry.length; i++ ) {
      (function () {
       var temp = i;//调用时局部变量
       pAry[i].onclick = function() {
          alert(temp);
        }
     })();
 }
}

4、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)

  function init5() {
    var pAry = document.getElementsByTagName("p");
    for( var i=0; i<pAry.length; i++ ) {
     pAry[i].onclick = function(arg) {
         return function() {//返回一个函数
         alert(arg);
       }
     }(i);
    }
}

闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

例子:

var name = "The Window";
   var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        alert(name);
                name +=" Change"; 
     };
    }
      };

object.getNameFunc()();的结果是:第一次弹出The Window,如果再获取一次第二次弹出The Window Change

例二:使用原生JS实现选项卡通过自定义属性。

请参考文章

https://www.jianshu.com/p/b5413ba72d8f

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值