之前学习冒泡和捕获时有些囫囵吞枣,加之很长一点时间没有做过dom类的工作,所以就遗忘了.这次因为项目中有一些dom操作需要我来写,所以又需要去研究一下.故总结出一些我个人的见解跟大家分享.如有理解不对的地方,欢迎各位不吝赐教,批评佐证.
什么是冒泡和捕获
冒泡和捕获是dom中的两种事件流,现代浏览器基本都遵循这两种事件流方式.
上面这句话可能比较抽象,废话不多说,那直接上代码,让我们通过现象去了解本质
<div id='far'>
<div id='son'></div>
</div>
//js es6
let far = document.getElementById('far');
let son = document.getElementById('son');
far.addEventListener('click',()=>console.log('我是far'));
son.addEventListener('click',()=>console.log('我是son'));
//执行以上代码,当点击son时,你会发现far的点击事件也被触发了.
很多人把这种现象叫做事件冒泡,起初我也是这样认为的.但是当我学习了事件流中的冒泡模型和捕获模型后,我就改变了想法(我认为,冒泡和捕获只是事件流执行的两种规则,后面会具体说).因为当你在点击son的时候,其实你点击的位置也在far的范围内,所以far的点击事件被触发是正常的.
那么也就可以很好的理解,为什么当我们不给son注册点击事件,而我们还是在son的范围内点击鼠标时,far依然会触发.
那么说到这里,我们就可以谈一谈冒泡啦.
此时,我们需要先考虑一个问题:
为什么先触发的不是far,而是son呢?
要回答这个问题,首先我们要知道,当事件触发时,事件会分为两个阶段,一个是冒泡阶段,另一个则是捕获阶段.整个事件流当中,先执行捕获,后执行冒泡.上面事例代码执行结果的原因就是事件是在冒泡阶段执行的(w3c在注册事件当中规定默认情况下在冒泡阶段执行).
在冒泡的阶段的执行顺序是按照 :从事件源 —>document 依次执行的
在刚才的例子当中的体现则是 son —>far(由内到外)
而捕获阶段则是: document—>事件源 依次执行
在下面例子中体现则是far—>son(从外到内)
//js es6
let far = document.getElementById('far');
let son = document.getElementById('son');
far.addEventListener('click',()=>console.log('我是far'),true);
son.addEventListener('click',()=>console.log('我是son'));
//addEventListenner函数的第三个参数可以控制事件在冒泡/捕获阶段执行. 当为false是,在冒泡阶段执行,为true时,则在捕获阶段执行.
以上就是所谓的冒泡和捕获.
为了更好的理解前面所说的内容,我们来通过一段代码分析来加深理解
<ul id="ul">
<li id='one'>222</li>
<li id='two'><a href="#" id='only'>111</a></li>
<li id='three'>333</li>
</ul>
const ul = document.getElementById('ul');
const li = document.getElementById('two');
const a = document.getElementById('only')
a.addEventListener(
'click',
()=>console.log('我是a标签'),
false);//冒泡阶段执行
li.addEventListener(
'click',
()=>console.log('我是li标签'),
false);//冒泡阶段执行
ul.addEventListener(
'click',
()=>console.log('我是ul标签'),
true);//捕获阶段执行
从以上代码可以看出来, 三个dom元素具有子父级的关系
ul > li > a
当我点击a标签时,执行结果如下:
ul —> a —>li
代码分析:
因为我手动设置ul在捕获阶段执行,li 和 a 在冒泡阶段执行.而前面我们讲过,事件流是先执行捕获后执行冒泡的.
说到这里,相信大家已经能够清晰的了解什么是冒泡和捕获了吧.
事件委托
我们来假设一个场景,我要给一个ul列表中的每一个li设置点击事件,应该怎么做呢?
一般做法 : 就是给每一个li添加一个点击事件. 但是这样做的弊端较大,冗余代码多,占用内存多等问题.
更好的办法 : 使用事件委托. 利用我们前面说到的,给父元素注册点击事件,点击内部子元素时,也会触发父元素的事件.代码如下:
<ul id="ul">
<li id='one'>111</li>
<li id='two'>222</li>
<li id='three'>333</li>
</ul>
let ul = document.getElementById('ul');
ul.addEventListener('click',(e)=>{
let ev = e || window.event;
let tag = ev.target||ev.srcElement;
switch(tag.id){
case 'one':
console.log("one")
break;
case 'two':
console.log('two')
break;
case 'three':
console.log('three')
break;
}
},true);
注:
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。