由于事件会在冒泡阶段向上传播到父节点,因些可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件代理(delegation),也可以叫做事件委托。简单来说,它是基于事件冒泡,只制定一个时间来处理程序,就可以管理某一类型的所有行为绑定事件。可以看出它们要用到事件目标的target和srcElement属性。
举一个简单的例子来说,页面上有如下代码,想要给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a->li->ul。现在我们给最外面的ul加上点击事件,那么里面的li,a执行点击事件的时候,都会冒泡到最外层的div上都会触发,这就是事件委托。
<ul>
<li>
<a href="javascript:;"></a>
</li>
</ul>
如下面的代码中,我们想实现点击一下按钮可以新增2个li,且每个li在点击的时候都能弹出我是第几个li这一需求给动态的元素绑定事件。
<link rel="stylesheet" href="./css/reset.min.css" />
<style>
.box {margin: 20px auto; 200px;}
.newList {box-sizing: border-box;border: 2px solid #bbbbbb;padding: 5px;}
.newList li { line-height: 35px;border-bottom: 1px dotted #cccccc;}
.createBtn {box-sizing: border-box;margin-top: 10px; 80px;height: 30px; border: 1px solid #cccccc;}
</style>
<div class="box">
<ul class="newList">
<li>我是第1个li</li>
<li>我是第2个li</li>
</ul>
<button class="createBtn">新增按钮</button>
</div>
我们用到jQuery可以更加方便的操作DOM,在没有用事件委托之前,我们用循环绑定。可以先写一个headle方法,在这个方法中,我们先获取所有的li,然后遍历每一个lis让其弹出“我是第几个li”。然后给按钮绑定事件,点击增加2个li。刚开始我们执行一次headle方法,然后给按键绑定事件增加li后再执行一次headle方法。这样一看我们操作DOM的次数过于频繁,引起浏览器重绘与重排的次数。这样很消耗性能。
<script src="../../node_modules/jquery/dist/jquery.min.js"></script>
<script>
let $newList = $(".newList"),
$lis = null,
$createBtn = $(".createBtn");
function handle() {
$lis = $newList.children("li");
$lis.each(function (index, item) {
$(item).click(function () {
alert(`我是第${index + 1}个li`);
});
});
}
handle();
let count = 2;
$createBtn.click(function () {
let str = ``;
for (let i = 0; i < 2; i++) {
count++;
str += `<li>我是第${count}个li</li>`;
}
$newList.append(str);
handle();
});
这里用父级newList做事件处理。当li点击时,由于冒泡机制,事件会冒泡到newList上,newList上的事件就会被触发。当然我们点击newList时这个事件也会触发,这时就需要用到Event对象提供的target属性,它可以返回事件的目标节点,我们称之为事件源,这时我们只是获取了当前节点的位置并不知道节点的名称,这时要用到tagName来获取具体是什么标签名(这里标签名是大写)具体代码如下,这样的话每次只执行一次DOM操作,大大减少了DOM的操作,进行了性能上的优化。但使用“事件委托”时,并不是把事件委托给的元素越靠近顶层就越好。如果DOM嵌套结构很深,事件冒泡通过大量祖先元素也会导致性能损失。
let $newList = $(".newList"), $createBtn = $(".createBtn"), count =2; $newList.click(function (ev) { let target = ev.target, $target = $(target); target.tagName === "LI" ? alert(`我是第${$target.index()+1}个li`) : null; }); $createBtn.click(function () { let str = ``; for (let i = 0; i < 2; i++) { count++; str += `<li>我是第${count}个li</li>`; } $newList.append(str); });