- A+
事件委托与事件对象
事件冒泡与事件捕获
事件流:用于描述页面接收事件的顺序。以下是事件流的两种不同方案:
事件冒泡:事件由最具体的元素逐级向上传递到最不具体的元素。
事件捕获:事件由最不具体的元素逐级向下传递到最具体的元素。
以上的两种事件流方案是截然相反的,分别由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事件流
捕获阶段: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>
很明显,上面的代码验证了网页的事件触发会存在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
元素添加了八个事件。利用事件委托的方式进行网页性能的优化,其效果不言而喻。
参考