JS事件冒泡与JS事件代理(事件委托)
- 1、事件冒泡
- 1.1 概念
- 1.2 要是不给子元素添加具体的oncilck处理方法,也能冒泡么?
- 1.3 子元素触发的事件冒泡会触发父元素所有的事件么?还是触发对应的事件?
- 1.4 那么我们应该如何组织这讨厌的事件冒泡机制呢?
- 2、事件委托(事件代理)
- 2.1 概念
- 2.2 for循环遍历给每个li添加事件
- 2.3 利用事件代理给li添加事件
1、事件冒泡
1.1 概念
通俗来讲,当触发(点击或者触摸之类的做法)有父元素的子元素的时候,事件会从事件源(被点击的子元素)开始逐级向上传播,触发父级元素的点击事件,一直会传到window。如果在某一层想要中止冒泡,使用 event.stopPropagation() 。下面见详细的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#parent {
width: 400px;
height: 400px;
background-color: blue;
display: flex;
justify-content: center;
align-items: center;
}
#child {
width: 200px;
height: 200px;
background-color: brown;
}
</style>
</head>
<body>
<div id="parent">
<div id="child"></div>
</div>
<script>
let parent = document.getElementById('parent')
parent.onclick = function () {
console.log('parent')
}
let child = document.getElementById('child')
child.onclick = function () {
console.log('child')
}
</script>
</body>
</html>
我们可以发现,当点击红色区域(子元素)的时候,父级元素的click事件也被触发了。
接下来看两个示例:
1.2 要是不给子元素添加具体的oncilck处理方法,也能冒泡么?
<script>
// let parent = document.getElementById('parent')
// parent.onclick = function () {
// console.log('parent')
// }
let child = document.getElementById('child')
child.onclick = function () {
console.log('child')
}
</script>
效果如下:
子元素在没有定义具体的click处理函数的时候仍然可以冒泡,触发父级元素的click事件。
1.3 子元素触发的事件冒泡会触发父元素所有的事件么?还是触发对应的事件?
<script>
let parent = document.getElementById('parent')
parent.onkeydown = function () {
console.log('parent')
}
let child = document.getElementById('child')
child.onclick = function () {
console.log('child')
}
</script>
结果如下:
我们发现只有相应的事件会发生事件冒泡,不相关的事件不受影响(注意由于click为鼠标的点击,所以同样会触发mousedown与mouseup等相关事件,同时发生冒泡!)
1.4 那么我们应该如何组织这讨厌的事件冒泡机制呢?
很简单,在事件触发时,会传入一个相应的event对象,其中有一个stopPropagation()方法可以阻止事件冒泡(IE中为cancleBubble=true),以下是详细代码:
<script>
let parent = document.getElementById('parent')
parent.onclick = function () {
console.log('parent')
}
let child = document.getElementById('child')
child.onclick = function (ev) {
var e = ev || window.event
// console.log(e)
console.log('child')
stopPropagation(e)
}
function stopPropagation (e) {
if (e.stopPropagation) {
e.stopPropagation()
} else {
e.cancelBubble = true
}
}
</script>
结果如下:
可以通过运行结果来看,事件冒泡成功被阻止了。
看到这里,相信小伙伴们可能会觉得事件冒泡很麻烦,还需要写个事件阻止冒泡行为。但凡事都有双刃剑,事件冒泡同时给我们带来的还有事件委托这一减少DOM操作的神器。
2、事件委托(事件代理)
2.1 概念
事件委托,首先按字面的意思就能看你出来,是将事件交由别人来执行,再联想到上面讲的事件冒泡,是不是想到了?对,就是将子元素的事件通过冒泡的形式交由父元素来执行。下面经过详细的例子来说明事件委托:
2.2 for循环遍历给每个li添加事件
在开发的时候可能会遇到这种情况:如给ul下的每个li都加一个事件,你可能会通过遍历来给每个栏目添加事件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="parentUl">
<li>我是子元素</li>
<li>我是子元素</li>
<li>我是子元素</li>
</ul>
<script>
var ul = document.getElementById('parentUl'),
li = ul.getElementsByTagName('li')
for (var i = 0; i < li.length; i++) {
li[i].onclick = function () {
alert(this.innerHTML)
}
}
</script>
</body>
</html>
这种方式来添加事件固然简单,但是需要多次操作DOM,如果有100、1000个同级的元素需要添加事件,这种方式就不可取了。
2.3 利用事件代理给li添加事件
事件委托是利用事件的冒泡原理来实现的,比如我们平时在给ul中的li添加事件的时候,我们都是通过for循环一个个添加,如果li很多个的话,其实就有点占内存了,这个时候可以用 事件代理来优化性能,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul id='ulid' onclick='clickFunc()'>
<li>第1个li</li>
<li>第2个li</li>
<li>第3个li</li>
<li>第4个li</li>
<li>第5个li</li>
</ul>
<script>
function clickFunc (ev) {
var ev = ev || window.event
console.log(ev)
var oLi = ev.srcElement || ev.target
if (oLi.nodeName.toLowerCase() == 'li') {
alert(oLi.innerText)
}
}
</script>
</body>
</html>
结果如下: