引言
通过对面试复盘以及最近自己的心态变化,我觉得还是得一步一步来。最近看到这么一段话:
当你想要开车去周游世界时,并不需要给自己的车装满足够跑完整个世界的油量,而只需要加满第一箱油就可以了,路上有那么多加油站,你随时都可以加油,路上有那么多的人,你也不用所有的事都只靠自己,想要一箱油就跑完整个世界的人遍地都是,但他们可能永远都不会出发,只有那些真正经历过的风景,才会让人真正变得丰富起来,哪怕看风景时的你狼狈不堪。
看完体会是我不应该急着让自己走的更快,而是应该走的更远。而最后的收获,都是需要脚踏实地耕耘的,因此,我会出产一套【亡羊补牢】系列博客。将知识理解透彻,也不掺杂其它东西了,认认真真地写好这一套专栏。觉得不错的小伙伴,可以持续关注哈~
问题引入
怎样给一个新增的dom节点绑定事件?
当看到这个问题时,我也是一脸懵逼的,没错,这就是一道腾讯面试题。如果你现在就有了思路,后续内容其实可以跳过了,可以查阅专栏其它文章了。(节约时间)
探讨问题
后来,网上一搜解决方案,原来与事件代理扯上了关系,不妨了解一下事件代理有什么作用吧。
事件委托(事件代理)的作用?
- 支持为同一个DOM元素注册多个同类型事件
- 可将事件分成事件捕获和事件冒泡机制
分析问题
作用一 支持为同一个DOM元素注册多个同类型事件
用以往注册事件的方法,如果存在多个事件,后注册的事件会覆盖先注册的事件
//index.html
<div id="div1"></div>
window.onload = function(){
let div1 = document.getElementById('div1');
div1.onclick = function(){
console.log('打印第一次')
}
div1.onclick = function(){
console.log('打印第二次')
}
}
可以看到第二个点击注册事件覆盖了第一个注册事件,只执行了console.log(‘打印第二次’);
用 addEventListener(type,listener,useCapture)
实现
- type: 必须,String类型,事件类型
- listener: 必须,函数体或者JS方法
- useCapture: 可选,boolean类型。指定事件是否发生在捕获阶段。默认为false,事件发生在冒泡阶段
<div id="div1"></div>
window.onload = function(){
let div1 = document.getElementById('div1');
div1.addEventListener('click',function(){
console.log('打印第一次')
})
div1.addEventListener('click',function(){
console.log('打印第二次')
})
}
可以看到两个注册事件都成功触发了。 useCapture是事件委托的关键,我们后面详解
作用二 可将事件分成事件捕获和事件冒泡机制
事件捕获
当一个事件触发后,从Window对象触发,不断经过下级节点,直到目标节点。在事件到达目标节点之前的过程就是捕获阶段。所有经过的节点,都会触发对应的事件
事件冒泡
当事件到达目标节点后,会沿着捕获阶段的路线原路返回。同样,所有经过的节点,都会触发对应的事件
通过例子理解两个事件机制:
例子:假设有body和body节点下的div1均有绑定了一个注册事件.
效果:
- 当为事件捕获(useCapture:true)时,先执行body的事件,再执行div的事件
- 当为事件冒泡(useCapture:false)时,先执行div的事件,再执行body的事件
//当useCapture为默认false时,为事件冒泡
<body>
<div id="div1"></div>
</body>
window.onload = function(){
let body = document.querySelector('body');
let div1 = document.getElementById('div1');
body.addEventListener('click',function(){
console.log('打印body')
})
div1.addEventListener('click',function(){
console.log('打印div1')
})
}
//结果:打印div1 打印body
//当useCapture为true时,为事件捕获
<body>
<div id="div1"></div>
</body>
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>
window.onload = function(){
let div = document.getElementById('div');
div.addEventListener('click',function(e){
console.log(e.target)
})
let div3 = document.createElement('div');
div3.setAttribute('class','div3')
div3.innerHTML = 'div3';
div.appendChild(div3)
}
</script>
<body>
<div id="div">
<div class="div1">div1</div>
<div class="div2">div2</div>
</div>
</body>
虽然没有给div1和div2添加点击事件,但是无论是点击div1还是div2,都会打印当前节点。因为其父级绑定了点击事件,点击div1后冒泡上去的时候,执行父级的事件。
这样无论后代新增了多少个节点,一样具有这个点击事件的功能。
最后
文章产出不易,还望各位小伙伴们支持一波!
往期精选:
其次,为方便大家更好阅读仓库笔记,搭建了个人网站(访问超逸の博客),方便小伙伴阅读玩耍~
学如逆水行舟,不进则退