DOM必修课之了解事件对象及事件传播机制
啥是事件对象及事件传播机制?在JavaScript中事件对象提供了与事件相关的信息,使开发者能够根据事件的具体情况做出相应的处理。而事件传播机制则决定了事件在DOM结构中的传递方式,使得开发者可以在不同层级的DOM元素上捕获和处理事件,实现交互和响应的灵活性和可控性。下面我们就围绕DOM中的事件来对其深入了解了解。
事件对象
-
事件对象是由浏览器在事件触发时所创建的对象,
这个对象封装了事件相关的各种信息
-
通过事件对象可以获取到事件的详细信息
比如:鼠标的坐标、键盘的按键等
-
浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,
所以我们可以在事件的回调函数中定义一个形参进行接收事件对象
例如:
box.addEventListener('click',function(event)=>{
console.log(event) // 当我们点击box所对应的DOM元素时event事件对象会被输出
})
其中的event为此次点击事件的事件对象
- 在DOM中存在着多种不同类型的事件对象
- 所有的事件对象有一个共同的祖先:Event
- event对象的属性较多具体可参考:Event - Web API | MDN (mozilla.org)
事件的冒泡
我们在处理事件相关操作时,事件冒泡是常会遇到或用到的一种机制。事件的冒泡是指当一个元素上的事件被触发时,该事件不仅会在当前元素上触发,还会向其父元素及祖先元素传播。
事件冒泡过程
假设我们在网页中有这样一段结构
<div class="box1">
<div class="box2">
<div class="box3">
</div>
</div>
</div>
当我们给它们添加事件时:
let box1 = document.querySelector('.box1')
let box2 = document.querySelector('.box2')
let box3 = document.querySelector('.box3')
box1.addEventListener('click',function(event){
console.log("我是box1")
})
box2.addEventListener('click',function(event){
console.log("我是box2")
})
box3.addEventListener('click',function(event){
console.log("我是box3")
})
在以上的代码结构基础之上,
-
当我们点击蓝色部分时我们可以看到控制输出的只有
我是box1
-
当我们点击红色部分时我们可以看到控制台输出了
我是box2
和我是box1
-
当我们点击绿色部分时我们可以看到控制台输出了
我是box3
、我是box2
和我是box1
通过以上例子我们可以看到,当我们点击一个子元素时,不仅会触发自身(box3)的点击事件,同时也触发了其父元素(box2)及祖先元素(box1)的点击事件,且它们的顺序时从里向外的,正如其名冒泡(一层一层地往外冒)
阻止事件冒泡
在有点时候我们并不希望事件冒泡的存在,所以我们可以通过事件对象上的event.stopPropagation()
去阻止事件冒泡。
box2.addEventListener('click',function(event){
console.log("我是box2")
event.stopPropagation()
})
// 在将box2中添加了停止冒泡方法后,
// 点击蓝色(box1)时,输出'我是box1'
// 点击红色(box2)时,输出'我是box2'
// 点击绿色(box3)时,输出'我是box3','我是box2'
我们可以发现的是当box2的点击事件触发后事件的传导就已经被提前结束了,box3的没有被阻止所以传递到了box2。
同时我们要注意的是事件的冒泡跟位置无关只跟结构有关。
<div class="box1">
<div class="box2">
<div class="box3" style="left: 300px;"></div>
</div>
</div>
当我们改变box3的位置后我们点击box3(绿色方块)同样会触发box1与box2的点击事件
- 事件的冒泡就是指事件的向上传导
- 当元素的某个事件被触发后,其祖先元素上的相同事件也会同时被触发
- 冒泡的存在大大简化了代码的编写,但是在一些场景下我们并不希望冒泡 我们可以通过事件对象停止事件冒泡
事件对象中的DOM元素
在事件对象中有多种获取事件有关DOM元素的方式,其中主要有:
event.target
:以该方式获取的DOM对象为触发事件的对象。event.currentTarget
:以该方式获取的DOM对象为绑定事件的对象。this
:同event.currentTarget
(特别注意的是当事件所绑定的回调函数为箭头函数时this
为全局对象window
)。
box1.addEventListener('click',function(event){
console.log(event.target,event.currentTarget)
})
同上,当我们将代码改成上述内容,当点击红色(box2)时,输出为<div class="box2"></div>,<div class="box1"></div>
,分别代指类名为box2的DOM元素(触发事件的对象)与类名为box1的DOM元素(绑定事件的对象)。
事件的委派
假设有这样一个场景:
<button id="btn">增加一个按钮</button>
<ul>
<li><button>按钮1</button></li>
<li><button>按钮2</button></li>
<li><button>按钮3</button></li>
</ul>
let btns = document.querySelectorAll('li button')
btns.forEach(item=>{
item.addEventListener('click',(event)=>{
console.log('我被点击了')
})
})
let btn = document.getElementById('btn')
let ul = document.querySelector('ul')
btn.addEventListener('click',()=>{
let num = ul.querySelectorAll('li button').length+1
ul.insertAdjacentHTML(
'beforeend',
`<li><button>按钮${num}</button></li>`
)
})
列表中有一组按钮,我想给每一个红色的按钮都添加一个点击事件,首先我们可以通过循环每个button元素来给每个按钮添加点击事件。
但是当我点击添加一个按钮后,多添加的点击按钮却没有被添加点击事件。我们很容易想到的是,只要我们获取到新添加的节点重新添加点击事件就行了。
这样虽然可以,但稍显麻烦,这时我们就可以巧妙地利用事件委派来解决了
我们可以将以上代码改为:
let ul = document.querySelector('ul')
let btn = document.getElementById('btn')
btn.addEventListener('click',()=>{
let num = ul.querySelectorAll('li button').length+1
ul.insertAdjacentHTML(
'beforeend',
`<li><button>按钮${num}</button></li>`
)
})
ul.addEventListener('click',(event)=>{
if([...ul.querySelectorAll('li button')].includes(event.target)){
console.log('我被点击了')
}
})
我们可以看到的是我们将事件写在了ul的点击事件上,并通过判断是否触发事件,就相当于ul替按钮完成了事件的触发,这并是事件的委派(将button上的事件委派到ul元素上),委派就是将本该绑定给多个元素的事件统一绑定到其父元素(或祖先元素)上进行处理。
以上只是做了个简单的示例,总结起来事件的委派有不少优点,主要有:
- 减少内存消耗和提高页面响应速度。尤其在动态添加大量子元素的场景下,无需为新元素重复绑定事件监听器。
- 只需在父元素上编写一次事件处理逻辑,即可管理所有子元素的事件,易于维护和扩展。
- 对于动态添加或删除的子元素,事件委托可以自动处理这些新元素的事件,无需额外的事件绑定操作。
事件的传播机制
在DOM中事件的传播可分为三个阶段:
-
捕获阶段(由祖先元素向目标元素进行事件捕获):事件从根节点开始,沿着DOM层次向下传播,直至到达事件的实际目标元素。
-
目标阶段(触发事件的对象):事件达到目标元素后,在目标元素上触发相应的事件处理函数。
-
冒泡阶段(由元素向祖先元素进行事件冒泡):事件从目标元素开始,再次向外层元素传播,直到达到DOM树的根节点。
默认情况下,事件不会在捕获阶段触发, 如果希望在捕获阶段触发事件,
可以将addEventListener
的第三个参数设置为true
一般情况下我们不希望在捕获阶段触发事件,所以通常不设置第三个参数。
本次的分享就到此结束啦!谢谢你的阅读😊😊😊,有错误的地方还望指正。
转载自:https://juejin.cn/post/7375054926584021003