JavaScript基础——事件流

来源:互联网 发布:帝国cms视频教程 编辑:程序博客网 时间:2024/06/06 03:36

事件是什么?

在JavaScript中事件就是用户与浏览器交互期间某个特殊的瞬间,如用户点击操作、输入、拖动、鼠标移动、窗口大小改变……

事件流

事件流就是在特殊瞬间,页面中不同部分的反映顺序,或是说解释顺序,对于click事件,#div1的反映(或解释,我比较喜欢称之为解释)是改变字体,而#div2的解释是改变颜色。#div1与#div2对同一click特定瞬间的解释顺序就构成了事件流。事件流这一概念存在的基础是同一特定瞬间,也就是用户一次的操作。一般情况下事件流有三种:事件冒泡、事件捕获、DOM事件流。

其实说的都是一回事,只不过在当时的时代背景下,对事件的解释顺序有三种定义方式。

事件的概念出现时,IE中的事件流是事件冒泡、Netscape Communicator提出的是事件捕获,而DOM标准则整合前两个事件流模型。IE8是最后一个仍然使用其专有事件系统的主要浏览器。IE9及其他主要浏览器都很好的支持了DOM事件流的规范。

事件冒泡

对于大多数人来说,事件冒泡是相对合理,容易接受的,如其名,事件的解释顺序像是冒泡一样,由底层到表层,也就是从具体到一般,如下面的html

<html><head></head><body><span style="white-space:pre"></span><divid=”div1”><span style="white-space:pre"></span><divid=”div2”></div><span style="white-space:pre"></span></div></body></html>
当在#div2上单击时,事件的传播顺序如下图:

事件捕获

事件捕获的解释顺序和事件冒泡是完全相反的,也可以打个比方,如其名,围捕总是从大圈到小圈,上面的例子在事件捕获中使用图解就是


无论是冒泡还是捕获都支持这样的观点:当事件发生时,事件的目标不是特定的一部分而是整个页面,也就是说当用户点击#div2时,用户不仅仅点击了#div2同时也点击了整个页面以及与#div2相关的部分。

DOM事件流

鉴于此,DOM标准在求同存异的基础上将二者整合在了一起,DOM事件流图解:


“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。也就是说事件捕获是由document向div2照射一束光,而事件冒泡是由div2向document照射,DOM标准则规定光是由document照射向div2的,但是我在div2加了一面镜子,又将光反射回了document。

关于事件流的几个问题

事件流的范围

对于下面的html

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Insert title here</title></head><body style="width:150px;height:150px;background:antiqueWhite;border:1px solid;">    <div id="div1"style="width:100px;height:100px;background:darkseagreen;border:1px solid;">        <div id="div2" style="width:50px;height:50px;background:aquamarine;border:1px solid;"></div>    </div></body><script type="text/javascript">    document.addEventListener('click',function(){        console.log('=== document ===');    },false);</script></html>
当点击页面中的任何部分都会在控制太输出相同的语句:=== document ===

验证了上面说的事件发生在页面而不是特定的部分。在此所说的页面是代码中的页面而不是直观看到的页面,如html

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Insert title here</title></head><body style="width:150px;height:150px;background:antiqueWhite;border:1px solid;">    <div id="div1"style="width:100px;height:100px;background:darkseagreen;border:1px solid;">        <div id="div2" style="width:50px;height:50px;background:aquamarine;border:1px solid;top:150px;position: absolute;"></div>    </div></body><script type="text/javascript">    document.getElementById('div1').addEventListener('click',function(){        console.log('=== div1 ===');    },false);</script></html>
页面:


在直观上div2处于div1的外面,代码中为div1注册的事件处理程序,但是点击div2,控制台中仍然会输出=== div1 ===。

结论:事件处理程序的传播顺序不是以页面表现为准而是以文档中节点的包含关系及顺序为准。

是事件传播不是事件处理程序的传播

刚开始学习的时候事件流的概念是很模糊的(对于我来说),总认为事件冒泡这个概念是对于在div2上注册的事件处理程序来说的,click事件的处理程序会冒泡到顶层……错了。

事件流的概念是针对DOM规范中的事件,或是各浏览器特有的事件,甚至是用户自定义的事件来说的,而不是事件的处理程序。事件和事件处理程序是两个完全不同的概念,事件是需要关心的特定瞬间,而事件处理程序是对特定瞬间的解释。具体来说事件是上面代码中的click,事件处理程序就是function对象。

事件流的三个阶段

在script中为各元素注册事件处理程序:

    var div2 = document.getElementById('div2');    var div1 = document.getElementById('div1');       document.body.addEventListener('click',function(){     console.log('body:false');    },false);    div1.addEventListener('click',function(){        console.log('div1:false');    },false);    div2.addEventListener('click',function(){        console.log('div2:false');    },false);    document.body.addEventListener('click',function(){        console.log('body:true');    },true);    div1.addEventListener('click',function(){        console.log('div1:true');    },true);    div2.addEventListener('click',function(){        console.log('div2:true');    },true);
控制台输出:(行号为手动添加,便于浏览)

1.body:true

2.div1:true

3.div2:false

4.div2:true

5.div1:false

6.body:false

根据输出可以看出事件的传播顺序,body→div1→div2(捕获阶段的结束+冒泡阶段的开始=处于目标阶段)→div1→body。1、2是事件捕获阶段,3、4是处于目标阶段,5、6是事件冒泡阶段。结论:事件流,先捕获再冒泡;捕获顺序:由外而内;冒泡顺序:由内而外。至于3、4的执行顺序则为处理程序的注册顺序(捕获和冒泡的顺序不一定)

DOM标准之所以将二者整合是为了尽可能的保证事件流的最大优点。无论是事件冒泡还是事件捕获,事件流只会经过每个元素一次,但是假如,点击了A,在A中显示随机的5个数,B中则显示这5个数的平均数,最后若B中显示的数在A中则将A中的这个数的颜色标记为红色,此时,无论是冒泡还是捕获都不能能很好的完成这个任务。只有往返式的事件流才能完成该项任务——DOM事件流。在事件捕获阶段可以根据需要做一些预处理,而在冒泡阶段则可以做相应的结束处理

但是在此三个阶段中重要的仍然是事件冒泡阶段,而并不是所有的事件都支持事件冒泡,而且利用事件冒泡可以适当的设计代码结构,以减少内存使用。如click事件不需要在每个元素上注册,在最外层元素中注册一个click事件处理程序,其余元素的click事件处理程序委托给最外层上的click事件就可以了,这样会大量的减少click事件对象的创建,达到减少内存使用的目的。

0 0