事件委托与事件对象
事件冒泡与事件捕获
事件流:用于描述页面接收事件的顺序。以下是事件流的两种不同方案:
事件冒泡:事件由最具体的元素逐级向上传递到最不具体的元素。
事件捕获:事件由最不具体的元素逐级向下传递到最具体的元素。
以上的两种事件流方案是截然相反的,分别由IE开发团队和Netscape开发团队提出。
添加事件处理的方法
HTML事件处理程序:利用特定HTML标签的事件属性(<input>
)。代码如下:
<input type="button" value="测试" onclick="test()">
<script>
let test = () => { // 利用标签属性添加事件
console.log('已被点击')
}
</script>
DOM0事件处理程序:利用DOM节点自带的事件属性。代码如下:
<input type="button" value="测试" id="test">
<script>
let test = document.querySelector('#test')
test.onclick = () => { // 利用onlick属性添加事件
console.log('已被点击')
}
</script>
DOM2事件处理程序(推荐):利用DOM节点(继承自EventTarget)的添加事件监听器方法。代码如下:
<input type="button" value="测试" id="test">
<script>
let test = document.querySelector('#test')
test.addEventListener('click', ()=>{ // 利用添加事件监听器方法
console.log('已被点击')
})
</script>
说明:在网页DOM编程中的继承关系:EventTarget
<= Node
<= Element
。因此上面的test
变量拥有addEventListener
方法
DOM2 Events事件流
![](https://markdown-1314387653.cos.ap-guangzhou.myqcloud.com/img/WechatIMG23.jpeg)
捕获阶段:Document
=> Element html
=> Element body
=> Element div
冒泡阶段:Element div
=> Element body
=> Element html
=> Document
我们首先了解EventTarget.addEventListener(type, listener, options)
这个方法的一些内部参数:
type
:监听事件的类型
listener
:接收一个回调函数,事件触发后会执行。
options
:里面有比较多的可选项参数,这里我们利用capture
(布尔值:默认为false)这个参数。表示监听的事件在捕获阶段会触发listener
执行。
接下来我们利用上面的方法还原上面DOM2事件流的捕获阶段与冒泡阶段,如下:
<div style="cursor: pointer">我是一个事件,请点击验证</div>
<script>
// 捕获阶段
document.addEventListener("click", () => {
console.log("捕获阶段1:Document") // document
}, true)
document.documentElement.addEventListener("click", () => {
console.log("捕获阶段2:Element html") // html
}, true)
document.body.addEventListener("click", () => {
console.log("捕获阶段3:Element body") // body
}, true)
document.querySelector("div").addEventListener("click", () => {
console.log("捕获阶段4:Element div") // div
}, true)
// 冒泡阶段
document.querySelector("div").addEventListener("click", () => {
console.log("冒泡阶段4:Element div") // div
}, false)
document.body.addEventListener("click", () => {
console.log("冒泡阶段5:Element body") // body
}, false)
document.documentElement.addEventListener("click", () => {
console.log("冒泡阶段6:Element html") // html
}, false)
document.addEventListener("click", () => {
console.log("冒泡阶段7:Document") // document
}, false)
</script>
![](https://markdown-1314387653.cos.ap-guangzhou.myqcloud.com/img/202212150039008.png)
很明显,上面的代码验证了网页的事件触发会存在DOM2事件流这一执行过程。我们点击了事件,这个事件经历了由捕获阶段再到冒泡阶段的传递。
事件对象常用属性和方法
方法属性 | 说明 |
---|---|
Event.target 只读 | 对事件原始目标的引用。 |
Event.type 只读 | 事件的类型,不区分大小写。 |
event.preventDefault | 取消默认事件(如果该事件可取消)。 |
event.stopPropagation | 停止冒泡,阻止事件在 DOM 中继续冒泡。 |
其中Event.target
最为常用,具体指代触发了相应事件的Node
节点目标。
事件委托的应用(实现hover
悬停变色效果)
需求:实现一个列表,鼠标进入或离开都会使列表子元素的背景颜色改变。
如果没有事件委托,我们一般实现这个需求应该这样做。如下:
<div class="list" style="height: 400px; width: 400px;">
<ul style="list-style: none; text-align: center;">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script>
let list = document.querySelectorAll('.list > ul > li');
// 遍历DOM集合,给每个li添加事件
list.forEach(element => {
element.addEventListener('mouseover', () => {
element.style.backgroundColor = 'green';
})
element.addEventListener('mouseout', () => {
element.style.backgroundColor = '';
})
});
</script>
我们遍历每个li
元素并为其添加鼠标移入与移出事件。目前总共添加了八个事件处理程序。
注意:在JavaScript中,事件处理程序的数量会影响页面的整体性能。
因此对上述实现方式我们有必要采取措施优化。利用事件委托优化如下:
// 点击li元素后会通过事件冒泡机制触发ul添加的click事件。
<div class="list" style="height: 400px; width: 400px;">
<ul style="list-style: none; text-align: center;">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script>
let list = document.querySelector('.list > ul');
// 直接给ul父元素添加事件即可
list.addEventListener('mouseover', (event) => {
if (event.target.nodeName.toLowerCase() === 'li') {
event.target.style.backgroundColor = 'green';
}
})
list.addEventListener('mouseout', (event) => {
if (event.target.nodeName.toLowerCase() === 'li') {
event.target.style.backgroundColor = '';
}
})
</script>
我们只给list
元素添加了两个事件,同样实现了需求。如果采用原始方式,我们一共给这些li
元素添加了八个事件。利用事件委托的方式进行网页性能的优化,其效果不言而喻。
参考