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

浅谈事件委托

程序员文章站 2022-05-06 10:05:26
...

1、事件委托

事件委托还有一个名字叫事件代理,JS高程上讲:事件委托就是利用事件冒泡,只制定一个事件处理程序,就可以管理某一类型的所有事件。

1.1 事件委托原理:事件冒泡机制

1.2 优点:

(1)大量减少内存占用,减少事件注册。 不需要为每个元素都添加监听函数。对于同一个父节点下面类似的子元素,可以通过委托给父元素的监听函数来处理事件

(2)新增元素实现动态绑定事件

1.3 实现方式:

(1)可用addEventListener(); //所有主流浏览器,除了IE8及更早IE版本。

​ 1)语法:element.addEventListener(event, function, useCapture);

event:必须。字符串,指定事件名。 不加’on’,如click

function:必须。指定要事件触发时执行的函数。

useCapture:可选。布尔值,指定事件是否在捕获或冒泡阶段执行(true-事件句柄在捕获阶段执行;false-默认-事件句柄在冒泡阶段执行)

​ 2)移除事件监听:element.removeEventListener(event, function,useCapture)。移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()添加的匿名函数无法移除。

​ 3)功能:可多次绑定同一个事件,并且不会覆盖上一个事件

浅谈事件委托

如果想让事件代理的效果跟直接给节点的事件效果一样怎么办?

比如说只有点击li才会触发?

解决方案

Event对象提供了一个属性叫target,可以返回事件的目标节点,我们称为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom。标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名。这样改下就只有点击li会触发事件了,且每次只执行一次dom操作。 之前的是当点击ul的时候,也是会触发的 .

ul.addEventListener("click",function(e) { 
    if(e.target && e.target.nodeName.toLowerCase() == "li") { //检查事件源e.target是否为Li 
        alert(123);
  } 
});

浅谈事件委托

(2)attachEvent() //IE8及IE更早版本

​ 1)用法:element.attachEvent(event,function);

event事件加’on’,如onClick

没有第三个参数,因为IE只有冒泡,没有反向冒泡

​ 执行顺序按照绑定的反序(先执行后绑定的方法)。

​ 2)移除事件监听:element.detachEvent(event,function)

2、细说事件冒泡

事件冒泡,这个事件是两面性的,有时候给我们带来bug,有时候优点也很明显。我们就一起来看看它的真面目。

首先看看事件冒泡是什么?

事件冒泡 :当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window 。(注意这里传递的仅仅是事件 并不传递所绑定的事件函数。所以如果父级没有绑定事件函数,就算传递了事件 也不会有什么表现 但事件确实传递了。)

下面来看个栗子:

var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
div2.onclick = function(){alert(2);};
div1.onclick = function(){alert(1);};//父级

//html代码
 <div id="div1">
    <div id="div2"></div>
 </div>

代码很简单,就是两个父子关系的div,然后分别加了点击事件,当我们在div2里面点击的时候,会发现弹出了一次2,接着又弹出了1,这说明点击的时候,不仅div2的事件被触发了,它的父级的点击事件也触发了,这种现象就叫做冒泡。点击了div1,自己父级的点击事件也会被触发。

再看个栗子

var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2") 
div1.onclick = function(){alert(2);}; // 父亲

//html代码 
<div id="div1"> 
	<div id="div2"></div> 
</div>

浅谈事件委托大家可以看一下效果图,相比于第一个例子,代码已经把儿子的点击事件去掉,只留下了父级的,测试的结果是当只点击了儿子,会弹出2,由此证明了当点击了儿子,父亲的点击事件被触发,执行了自己绑定的函数。由于一些人会以为显示出来儿子在父亲里面的时候,自然点了儿子相当于点了父亲,所以这个例子我故意把两个盒子绝对定位在了两个不同的位置,所以点击事件给页面显示出来的位置是没关系的,而是跟html代码中的位置有关系。

可能有人会有疑惑下面这种情况,为啥没有弹出两次:

var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
div2.onclick = function(){alert(1);}; 
div1.onclick = function(){};  //父亲

//html代码

 <div id="div1">
    <div id="div2"></div>
 </div>

这里我们要注意,我们传递的仅仅是事件触发,也就是说当点击div2仅仅触发了父级的点击事件,并没有把自己的绑定的函数给父级,父级的执行情况,取决于自己所绑定的函数,因为在这里它绑定的函数是空,自然没什么表现。有些人在这里有误解,所以强调一下。

以上大概就是冒泡的形式了,而大多数时候,冒泡也带来了一些困扰,下面我们看一个栗子:

浅谈事件委托浅谈事件委托我们的需求是点击上面红色的面板让下面粉色的面板显示,点击其他地方再让粉丝面板隐藏。于是我们会这样写js代码:

var div2 = document.getElementById("div2");
var div1 = document.getElementById("div1");

div2.onclick = function(){   //红色面板事件
	div1.style.display = "block"; 
};
document.onclick = function(){ 
	div1.style.display = "none"; 
}

这个时候我们测试发现,怎么点击红色面板,粉丝面板都不会显示了,为什么呢?就是冒泡的原因,我们来分析下代码,当点击div2的时候,他会触发父亲级别的点击事件,然后一层一层的往上传,所以document的点击事件自然也被触发了,然后执行了自己身上的绑定事件,让粉色面板消失。所以当你点击div2的时候首先,让粉丝面板显示,只是事件执行太快了,很快又执行了document的点击事件,让面板隐藏。 有兴趣的可以再两个事件之间加一个弹出,就可以测试。

那么这个时候我们肯定不希望有冒泡了,所以解决办法就是取消冒泡了:(后来补充)

取消事件冒泡有两种方式:

标准的W3C 方式:e.stopPropagation(); 这里的stopPropagation是标准的事件对象的一个方法,调用即可

非标准的IE方式:ev.cancelBubble=true; 这里的cancelBubble是 IE事件对象的属性,设为true就可以了

通常我们会封装这样一个函数:

function stopBubble(e) {
    //如果提供了事件对象,则这是一个非IE浏览器
   if ( e && e.stopPropagation )
      //因此它支持W3C的stopPropagation()方法
      e.stopPropagation();
  else
  //否则,我们需要使用IE的方式来取消事件冒泡
    window.event.cancelBubble = true;
}

这个时候需要用到事件对象里的 cancelBubble属性,把它设为true即可,ev.cancelBubble=true;所以改进后的代码如下:

var div2 = document.getElementById("div2");
var div1 = document.getElementById("div1");

div2.onclick = function(ev){  // 红色面板加事件
    div1.style.display = "block";     
    stopBubble(ev);//这样就不会再冒泡给父级了 
}; 
document.onclick = function(){      
    div1.style.display = "none"; 
} 
function stopBubble(e) {
    //如果提供了事件对象,则这是一个非IE浏览器
   if ( e && e.stopPropagation )
      //因此它支持W3C的stopPropagation()方法
      e.stopPropagation();
  else
  //否则,我们需要使用IE的方式来取消事件冒泡
    window.event.cancelBubble = true;
}