概述
什么叫事件委托?
- 事件委托就是利用事件冒泡,只指定一个事件处理程序,管理当前绑定下的所有元素的事件。
- 也可以这么理解。比如一个宿舍的同学同时快递到了,一种方法就是他们
一个个去领取
,还有一种方法就是把这件事情委托给宿舍长
,让一个人
出去拿好所有快递,然后再根据收件人一 一分发
给每个宿舍同学;取快递就是一个事件
,每个同学指的是需要响应事件的 DOM
元素,而出去统一领取快递
的宿舍长就是代理的元素
,所以真正绑定事件
的是这个元素,按照收件人分发快递的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个。 - 一个事件触发后,会在子元素和父元素之间传播。这种传播分成三个阶段。
(1)捕获阶段:从window对象传导到目标节点(上层传到底层)称为“捕获阶段”,捕获阶段不会响应任何事件;
(2)目标阶段:在目标节点上触发,称为“目标阶段”
(3)冒泡阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”。事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层。
事件委托的优点
例子
给每个li执行点击事件
<ul id="ul1">
<li a='1231dd' id="1" class="1.1">111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
传统方式
- 获取
ul
- 通过获取到
ul
去获取下面的所有li
- 循环
li
的长度,依次去注册事件
window.onload = function () {
var oUl = document.getElementById("ul1");
var oLi = oUl.getElementsByTagName('li');
for (let i = 0; i < oLi.length; i++) {
oLi[i].onclick = function () {
console.log(i, oLi[i]);
}
}
}
事件委托
- 这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发
- Event对象提供了一个属性叫
target
,可以返回事件的目标节点,我们成为事件源,也就是说,target
就可以表示为当前
的事件操作的dom
,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target
,IE浏览器用event.srcElement
,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName
来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题)。
oUl.onclick = function (ev) {
var ev = ev || window.event
var target = ev.target || ev.srcElement
if (target.nodeName.toLowerCase() === 'li') {
console.dir(target);
}
}
- 加了个判断,只会nodeName===li才会有效果,不会影响其他。
动态增加节点
<input type="button" name="" id="btn" value="添加" />
<ul id="ul1">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
传统方式
- 移入li,li变红,移出li,li变白,这么一个效果,然后点击按钮,可以向ul中添加一个li子节点
window.onload = function () {
var oBtn = document.getElementById("btn");
var oUl = document.getElementById("ul1");
var aLi = oUl.getElementsByTagName('li');
var num = 4;
for (var i = 0; i < aLi.length; i++) {
aLi[i].onmouseover = function () {
this.style.background = 'red';
};
aLi[i].onmouseout = function () {
this.style.background = '#fff';
}
}
oBtn.onclick = function () {
num++;
var oLi = document.createElement('li');
oLi.innerHTML = 111 * num;
oUl.appendChild(oLi);
};
}
- 这是一般的做法,但是你会发现,新增的li是没有事件的,说明添加子节点的时候,事件没有一起添加进去,这不是我们想要的结果,那怎么做呢?一般的解决方案会是这样,将for循环用一个函数包起来,命名为initLi,如下:
window.onload = function(){
var oBtn = document.getElementById("btn");
var oUl = document.getElementById("ul1");
var aLi = oUl.getElementsByTagName('li');
var num = 4;
function initLi() {
for(var i=0; i<aLi.length;i++){
aLi[i].onmouseover = function(){
this.style.background = 'red';
};
aLi[i].onmouseout = function(){
this.style.background = '#fff';
}
}
}
initLi();
oBtn.onclick = function(){
num++;
var oLi = document.createElement('li');
oLi.innerHTML = 111*num;
oUl.appendChild(oLi);
initLi();
};
}
- 功能实现了。但是增加了一个dom操作,在性能方面跟事件委托存在一定的差异。
事件委托方式
- 事件委托的方式,新添加的子元素是带有事件效果的,我们可以发现,当用事件委托的时候,根本就不需要去遍历元素的子节点,整体只注册一次ul事件,并没有给每个li都注册个监听。这样可以大大的减少dom操作,这就是
事件委托
的意义。
window.onload = function () {
var oBtn = document.getElementById("btn");
var oUl = document.getElementById("ul1");
var aLi = oUl.getElementsByTagName('li');
var num = 4;
oUl.onmouseover = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == 'li') {
target.style.background = "red";
}
};
oUl.onmouseout = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == 'li') {
target.style.background = "#fff";
}
};
oBtn.onclick = function () {
num++;
var oLi = document.createElement('li');
oLi.innerHTML = 111 * num;
oUl.appendChild(oLi);
};
}