JavaaScript----事件流的处理方式
一、事件流
当你点击一个按钮时,实际上不光点击了这个按钮,还点击了它的容器以及整个页面。这个点击产生了不止一个事件。而是一个事件流。
事件流,是指当事件发生时,会在发生事件的元素节点与DOM树根节点之间按照特定的顺序进行传播。
(1)网景(Netscape):采用事件捕获方式
(2)微软(Microsoft):采用冒泡方式
(3)W3C:采用中和方式(事件前期采用捕获方式:只捕获不处理,事件后期阶段采用冒泡)
1.1 事件冒泡
IE 事件流被称为事件冒泡,事件被定义为从最具体的元素开始触发,然后向上传播至没有那么具体的元素 (文档)。比如有如下 HTML 页面:
<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>
在点击页面中的 <div> 元素后,click 事件会以如下顺序发生:
<div> == > <body> == > <html> ==> document
所有现代浏览器都支持事件冒泡,只是在实现方式上会有一些变化。 IE5.5 及早期版本会跳过 <html> 元素(从 <body> 直接到document)。现代浏览器中的事件会一直冒泡到 window 对象。
1.2 事件捕获
Netscape 团队提出了名为事件捕获的事件流。事件捕获的意思是最不具体的节点最先收到事件,而最具体的节点最后收到事件。如果前面的例子使用事件捕获,则点击 <div>元素会以下列顺序触发click事件:
document ==> <thml> == > <body> == > <div>
事件捕获是 Netscape 唯一的事件流模型,但事件捕获在所有现代浏览器上也都支持。实际上,所有浏览器都是从 window 对象开始捕获事件,而 DOM2 Events 规范规定的是从 document 开始。实际当中几乎不会使用事件捕获。建议使用事件冒泡,特殊情况下可以使用事件捕获。
1.3 DOM 事件流
DOM2 Events 规范规定事件流分为3个阶段:① 事件捕获 ② 到达目标 ③ 事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是事件冒泡,最迟要在这个阶段响应事件。仍以前面那个简单的 HTML 为例,点击 <div> 元素会以下图的顺序触发事件。
在 DOM 事件流中,实际的目标( <div> 元素)在捕获阶段不会接收到事件。
- 事件捕获阶段: 从 document 到 <html> 再到 <body> 就结束了。
- 达到目标阶段: 这个阶段会在 <div>元素上触发事件。
- 事件冒泡阶段: 通常在事件处理时被认为是冒泡阶段的一部分。然后,冒泡阶段开始, 事件反向传播至文档。
大多数支持 DOM 事件流的浏览器实现了一个小小的拓展。虽然 DOM2 Events 规范明确捕获阶段不命中事件目标,但现代浏览器都会在捕获阶段在事件目标上触发事件。最终结果是在事件目标上有两个机会来处理事件。
所有现代浏览器都支持 DOM 事件流
二、事件处理程序
不同的绑定事件方法,造就了不同的事件阶段(冒泡/捕获)
情景一:直接在HTML中绑定事件
<body onclick="console.log('我是body')">
<button onclick="console.log('我是button')">
<span onclick="console.log('我是span')">点我</span>
</button>
</body>
效果:
从上图可看出,当我们点击span标签以后,上层button身上的事件以及body身上的事件都会按冒泡顺序执行
情景二:[domNode].onclick()方式 —— DOM0级
html:
<body>
<button>
<span>点我</span>
</button>
</body>
javascript:
var body = document.getElementsByTagName('body')[0];
var button = document.getElementsByTagName('button')[0];
var span = document.getElementsByTagName('span')[0];
body.onclick = function() {console.log('我是body')};
button.onclick = function() {console.log('我是button')};
span.onclick = function() {console.log('我是span')};
效果:
我们发现,DOM0级绑定事件的方式依然是冒泡的形式
情景三:[domNode].addEventListener()方式 —— DOM2级
addEvenListener(事件名,事件触发后的回调,布尔值)
false(默认):表示在冒泡阶段调用事件处理程序
true:表示在捕获阶段调用事件处理程序
html:
<body>
<button>
<span>点我</span>
</button>
</body>
javascript:
var body = document.getElementsByTagName('body')[0];
var button = document.getElementsByTagName('button')[0];
var span = document.getElementsByTagName('span')[0];
function theName() {console.log('我是' + this.nodeName)};
body.addEventListener('click',theName,false);
button.addEventListener('click',theName,false);
span.addEventListener('click',theName,false);
效果:
我们可以看到当addEventListener的第三个参数为false(不写默认也是false)的时候,事件流为冒泡。
当我们把第三个参数改为true时:
body.addEventListener('click',theName,true);
button.addEventListener('click',theName,true);
span.addEventListener('click',theName,true);
我们发现,执行顺序为body ——> button ——> span,为事件捕获阶段。
DOM2级规定:
- 事件流包括三个阶段(1.捕获阶段 2.目标阶段 3.冒泡阶段)
- 执行循序应遵循:捕获阶段 ——> 目标阶段 ——> 冒泡阶段
也就是说当你点击目标元素后,不会立刻执行触发事件,而是先执行事件捕获阶段 ——> 然后处于目标阶段(触发事件) ——> 事件冒泡阶段
我们来看一道例题:当我们点击cat猫咪时的输出顺序?
html:
<body>
<div class="grandfather">grandfather爷爷
<div class="father">father爸爸
<div class="son">son儿子
<div class="cat">cat猫咪</div>
</div>
</div>
</div>
</body>
javascript:
var grandfather = document.getElementsByClassName('grandfather')[0]
var father = document.getElementsByClassName('father')[0]
var son = document.getElementsByClassName('son')[0]
var cat = document.getElementsByClassName('cat')[0]
function theName() {
console.log('我是' + this.className);
}
cat.addEventListener('click', theName, false)//冒泡
son.addEventListener('click', theName, true)//捕获
father.addEventListener('click', theName, true)//捕获
grandfather.onclick = theName//冒泡
结果:
做题思路:
因篇幅问题不能全部显示,请点此查看更多更全内容