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.eventconsole.log(ev)var oLi = ev.srcElement || ev.targetif (oLi.nodeName.toLowerCase() == 'li') {alert(oLi.innerText)}}</script>
</body></html>
结果如下: