什么是事件?(敲黑板)
事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。(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高级程序设计》一书,博主在这里也是做一次总结,巩固一下相关知识,同时也希望没接触过事件流的童鞋们,有一个大概的概念。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。