1

什么是事件?(敲黑板)

事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。(by 《JavaScript高级程序设计》)
比如鼠标点击,双击,滚动条滑动...

什么是事件流?

先来看一个简单的例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div class="main">
        <div class="btn-wrap">
            <button id="btn">点击</button>
        </div>
    <div>
</body>
</html>

这时候我们点击btn的同时,也可以视为同时点击了btn的容器元素,甚至单击了整个页面。
事件流指的是从页面接收事件的顺序。
关于事件流,IE和Netscape提出了差不多相反的概念,IE提出的就是广为人知的事件冒泡流,而Netscape提出的则是事件捕获流

1. 事件冒泡

事件冒泡,即事件开始时由最具体的元素接收,如上面例子中的btn,然后逐渐向上级传播到较为不具体的节点。DOM2级事件规定addEventListener方法的第三个参数设为false,表示事件在冒泡阶段触发。

注:使用频繁的事件委托实际上也是利用了事件冒泡。

还是相同的DOM结构为例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div class="main">
        <div class="btn-wrap">
            <button id="btn">点击</button>
        </div>
    <div>
    <script type="text/javascript">
        var btn=document.querySelector("#btn"),
            btnWrap=document.querySelector(".btn-wrap"),
            main=document.querySelector(".main"),
            body=document.querySelector("body"),
            html=document.querySelector("html");
            btn.addEventListener("click",function(){
                console.log("你点击了ID为btn的button元素!");
            },false);
            btnWrap.addEventListener("click",function(){
                console.log("你点击了class为btn-wrap的DIV元素!");
            },false);
            main.addEventListener("click",function(){
                console.log("你点击了class为main的DIV元素!");
            },false);
            body.addEventListener("click",function(){
                console.log("你点击了body元素!");
            },false);
            html.addEventListener("click",function(){
                console.log("你点击了html元素!");
            },false);
            document.addEventListener("click",function(){
                console.log("你点击了document对象!");
            },false);
    </script>
</body>
</html>

如果我们点击btn,那么这个click事件的传播顺序如下:
图片描述

也就是,click事件首先在btn元素上触发,而这个元素就是我们单击的元素,然后click事件沿DOM树向上传播,在每一级的节点都会发生,直至传播到document对象。
所有现代浏览器都支持事件冒泡。

2. 事件捕获

事件捕获,即事件从不太确定的节点接收,然后向下传播到最具体的节点,事件捕获的用意在于在事件到达预期目标之前捕获它。DOM2级事件规定addEventListener方法的第三个参数设为true,表示事件在捕获阶段触发。
还是相同的DOM结构为例:
将参数改为true

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div class="main">
        <div class="btn-wrap">
            <button id="btn">点击</button>
        </div>
    <div>
    <script type="text/javascript">
        var btn=document.querySelector("#btn"),
            btnWrap=document.querySelector(".btn-wrap"),
            main=document.querySelector(".main"),
            body=document.querySelector("body"),
            html=document.querySelector("html");
            btn.addEventListener("click",function(){
                console.log("你点击了ID为btn的button元素!");
            },true);
            btnWrap.addEventListener("click",function(){
                console.log("你点击了class为btn-wrap的DIV元素!");
            },true);
            main.addEventListener("click",function(){
                console.log("你点击了class为main的DIV元素!");
            },true);
            body.addEventListener("click",function(){
                console.log("你点击了body元素!");
            },true);
            html.addEventListener("click",function(){
                console.log("你点击了html元素!");
            },true);
            document.addEventListener("click",function(){
                console.log("你点击了document对象!");
            },true);
    </script>
</body>
</html>

如果我们点击btn,那么这个click事件的传播顺序如下:
图片描述

在事件捕获过程中,document对象首先接收到click事件,然后事件沿着DOM树依次向下传播。
目前支持事件捕获流的浏览器有:IE9,Safari,Chrome,Opera,Firefox。
由于老版本浏览器不支持事件捕获,建议大家更多的是用事件冒泡,在有特殊需要时再使用事件捕获。

3. DOM事件流

根据DOM2级事件规定,事件流应该包括三个阶段,事件捕获阶段,处于目标阶段和事件冒泡阶段。
还是相同的DOM结构为例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div class="main">
        <div class="btn-wrap">
            <button id="btn">点击</button>
        </div>
    <div>
    <script type="text/javascript">
        var btn=document.querySelector("#btn"),
            btnWrap=document.querySelector(".btn-wrap"),
            main=document.querySelector(".main"),
            body=document.querySelector("body"),
            html=document.querySelector("html");
            //冒泡
            btn.addEventListener("click",function(){
                console.log("你点击了ID为btn的button元素!");
            },false);
            btnWrap.addEventListener("click",function(){
                console.log("你点击了class为btn-wrap的DIV元素!");
            },false);
            main.addEventListener("click",function(){
                console.log("你点击了class为main的DIV元素!");
            },false);
            body.addEventListener("click",function(){
                console.log("你点击了body元素!");
            },false);
            html.addEventListener("click",function(){
                console.log("你点击了html元素!");
            },false);
            document.addEventListener("click",function(){
                console.log("你点击了document对象!");
            },false);

            //捕获
            btn.addEventListener("click",function(){
                console.log("你点击了ID为btn的button元素!");
            },true);
            btnWrap.addEventListener("click",function(){
                console.log("你点击了class为btn-wrap的DIV元素!");
            },true);
            main.addEventListener("click",function(){
                console.log("你点击了class为main的DIV元素!");
            },true);
            body.addEventListener("click",function(){
                console.log("你点击了body元素!");
            },true);
            html.addEventListener("click",function(){
                console.log("你点击了html元素!");
            },true);
            document.addEventListener("click",function(){
                console.log("你点击了document对象!");
            },true);
    </script>
</body>
</html>

如果我们点击btn,那么这个click事件的传播顺序如下:
图片描述

在DOM事件流中,实际的目标btn不会接收到事件。这意味着在捕获阶段,事件从document到btn-wrap就停止了,下一阶段是“处于目标”阶段,于是事件在btn上发生,然后冒泡阶段发生,事件又传播回文档。

注:多数支持DOM事件流的浏览器都实现了一种特定行为,即使DOM2级事件规范明确要求捕获阶段不会涉及目标阶段,IE9,Safari,Chrome,Firefox,Opera9.5及更高版本都会在事件捕获阶段触发事件对象上的事件,这也是上图btn被触发两次的原因。
IE9,Safari,Chrome,Firefox,Opera都支持DOM事件流,IE8及更早版本不支持DOM事件流。

本文知识点大多来自《JavaScript高级程序设计》一书,博主在这里也是做一次总结,巩固一下相关知识,同时也希望没接触过事件流的童鞋们,有一个大概的概念。


sivan_
521 声望14 粉丝