事件委托:利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行!支持为同一个DOM元素注册多个同类型事件。
说到事件冒泡,那么就要说一下事件捕获了。
-
事件捕获 当一个事件触发后,从Window对象触发,不断经过下级节点,直到目标节点。在事件到达目标节点之前的过程就是捕获阶段。所有经过的节点,都会触发对应的事件
-
事件冒泡 当事件到达目标节点后,会沿着捕获阶段的路线原路返回。同样,所有经过的节点,都会触发对应的事件
当用户触发一个事件时,浏览器的所有详细信息都存在一个叫event的对象上。我们把他叫事件对象,所有事件在绑定方法的时候,天生自带一个参数就叫event。
document.οnclick=function(e){
var e=e||window.event
}
正常情况下,如果不加限制,事件都是进行事件冒泡机制的.
如下,先打印div1,后打印body。
<body>
<div id="div1">11</div>
</body>
<script>
window.onload = function(){
let body = document.querySelector('body');
let div1 = document.getElementById('div1');
body.onclick = function(){
console.log('打印body')
}
div1.onclick = function(){
console.log('打印div1')
}
}
//打印div1
// 打印body
</script>
要是想要进行事件捕获怎么办呢?
<div id="div1">11</div>
window.onload = function(){
let div1 = document.getElementById('div1');
div1.onclick = function(){
console.log('打印第一次')
}
div1.onclick = function(){
console.log('打印第二次')
}
}
//只打印第二次
-
标准浏览器用addEventListener(type,listener,useCapture)实现
- type: 必须,String类型,事件类型
- listener: 必须,函数体或者JS方法
-
useCapture: 可选,boolean类型。指定事件是否发生在捕获阶段。默认为false,事件发生在冒泡阶段
- 以往注册事件的方法,如果存在多个事件,后注册的事件会覆盖先注册的事件
<div id="div1">11</div>
window.onload = function(){
let div1 = document.getElementById('div1');
div1.addEventListener('click',function(){
console.log('打印第一次')
})
div1.addEventListener('click',function(){
console.log('打印第二次')
})
}
//打印第一次
//打印第二次
这样后面的就不会覆盖前面的了。
如下所示:把useCapture的值设为true,就会进行事件捕获了。这样body就会先输出。
<body>
<div id="div1"></div>
</body>
<script>
window.onload = function(){
let body = document.querySelector('body');
let div1 = document.getElementById('div1');
body.addEventListener('click',function(){
console.log('打印body')
},true)
div1.addEventListener('click',function(){
console.log('打印div1')
})
}
//打印body
//打印div1
</script>
那什么样的事件可以用事件委托,什么样的事件不可以用呢?
- 适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
- 值得注意的是,mouseover 和 mouseout 虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。
- 不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说 focus,blur 之类的,本身就没用冒泡的特性,自然就不用事件委托了。
事件委托的优点:
- 提高性能:每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。
- 动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。 例子解析:
target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。
<ul>
<li>11</li>
<li>22</li>
<li>33</li>
</ul>
// good
document.querySelector('ul').onclick = (event) => {
let target = event.target
if (target.nodeName === 'LI') {
console.log(target.innerHTML)
}
}
// bad
document.querySelectorAll('li').forEach((e) => {
e.onclick = function() {
console.log(this.innerHTML)
}
})
就这样父级绑定了点击事件,点击子元素后冒泡上去的时候,执行父级的事件。
事件委托怎么取索引?
<ul id="ul">
<li>aaaaaaaa</li>
<li>事件委托了 点击当前,如何获取 这个点击的下标</li>
<li>cccccccc</li>
</ul>
<script>
window.onload = function () {
var oUl = document.getElementById("ul");
var aLi = oUl.getElementsByTagName("li");
oUl.onclick = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == "li") {
var that = target;
var index;
for (var i = 0; i < aLi.length; i++)
if (aLi[i] === target) index = i;
if (index >= 0) alert('我的下标是第' + index + '个');
target.style.background = "red";
}
}
}
</script>