2022-03-12
阅读:70
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事件会以如下顺序发生
buttondivbodyhtmldocument
也就是说,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事件)。这意味着只要页面渲染出可以点击的元素,就可以无延迟的起作用。