2022-03-12
阅读:64
Javascript与HTML的交互是通过事件实现的,事件代表文档或浏览器窗口中某个具有意义的时刻。可以使用仅在事件发生时执行的监听器(也叫处理程序)订阅事件。在传统软件工程领域,这个模型叫观察者模式,其能够做到页面行为(在JavaScript中定义)与页面展示(在HTML和CSS中定义)的分离。
当你点击一个按钮时,实际上不光点击了这个按钮,还点击了它的容器以及整个页面。
事件流描述了页面接收事件的顺序。IE和Netscape开发团队提出了几乎完全相反的事件流方案。IE将支持事件冒泡流,而Netscape Communicator将支持事件捕获流。
IE事件流被称为事件冒泡,这是因为事件被定义为从最具体的元素(文档树种最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)。比如有如下HTML页面:
<!DOCTYPE html>
<html onclick="handleClick('html')">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body onclick="handleClick('body')">
<div class="box" onclick="handleClick('div')">
<button onclick="handleClick('button')">点击</button>
</div>
<script>
//处理点击事件
function handleClick(e){
console.log(e)
}
//监听document点击事件
document.addEventListener("click",function(){
console.log('document')
})
</script>
</body>
</html>
在点击页面中的button
元素后,click
事件会以如下顺序发生
button
div
body
html
document
也就是说,button
元素,即被点击的元素,最先触发click
事件。然后,click
事件沿着DOM树一路向上,在经过的每个节点上依次触发,直至到达document
对象。
所有现代浏览器都支持事件冒泡,只是在实现方式上会有一些变化。IE5.5及早期版本会跳过
html
元素(从body
直接到document
)。现代浏览器中的事件会一直冒泡到window
对象。
Netscape Communicator 团队提出了另一种名为事件捕获的事件流。事件捕获的意思是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。
在事件捕获中,click
事件首先由document
元素捕获,然后沿DOM树依次向下传播,直至到达实际目标元素。
虽然这是Netscape Communicator 唯一的事件流模型,但事件捕获得到了所有现代浏览器的支持。实际上,所有浏览器都是从window
对象开始捕获事件,而DOM2 Events 规范规定的是从document
开始。
DOM2 Events 规范规定事件流分为三个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡。最迟要在这个阶段响应事件。
在DOM事件流中,实际的目标在捕获阶段不会接收到事件。下一阶段,即会在实际元素上触发事件的“到达目标”阶段,通常在事件处理时被认为是冒泡阶段的一部分。然后,冒泡阶段开始,事件反向传播至文档。
事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件
比如有以下HTML:
<ul id="list">
<li id="item1">1</li>
<li id="item2">2</li>
<li id="item3">3</li>
</ul>
这里的HTML包含3个列表项,在被点击时应该执行某个操作。对此,通常的做法是像这样指定3个事件处理程序
const item1 = document.getElementById('item1')
const item2 = document.getElementById('item2')
const item3 = document.getElementById('item3')
item1.addEventListener("click",function(event){
//TODO
})
item2.addEventListener("click",function(event){
//TODO
})
item3.addEventListener("click",function(event){
//TODO
})
如果对页面中所有需要使用onclick
事件处理程序的元素都如法炮制,结果就会出现大片雷同的指定事件处理程序的代码。使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决问题。比如:
const list = document.getElementById('list')
list.addEventListener("click",function(event){
//event.target 可以获取事件的目标元素
const target = event.target
console.log(target)
})
使用事件委托的好处:
document
对象随时可用,任何时候都可以给它添加事件处理程序(不用等待DOMContentLoaded或load事件)。这意味着只要页面渲染出可以点击的元素,就可以无延迟的起作用。