JavaScript Event学习第八章 事件的顺序


Posted in Javascript onFebruary 07, 2010

基本问题很简单。假设你的一个元素包含在另外一个元素中。

----------------------------------- 
| element1 | 
| ------------------------- | 
| |element2 | | 
| ------------------------- | -----------------------------------

这两个元素都有onclick事件处理程序。如果用户在element2上面单击那么在元素2和元素1上都触发了单击事件。但是哪个事件先发生呢?哪个事件处理程序会先执行呢?换句话说,事件顺序(event order)是什么呢?

两种模式
毫无疑问的,Netscape和微软在过去那段很糟糕的日子里都做出了自己的决定。
Netscape说element1先发生的。这叫事件捕获(event capturing)。
微软觉得element2先发生的。这叫事件冒泡(event bubbling)。
这两种事件顺序刚好相反。IE只支持事件冒泡。Mozilla,Opera 7和Konqueror两种都支持。早一些的Opear和iCab浏览器两个都不支持。

事件捕获
当你使用事件捕获的时候

---------------| |----------------- 
| element1 | | | 
| --------- --| |----------- | 
| |element2 \ / | | 
| ------------------------- | 
| Event CAPTURING | 
-----------------------------------

element1的事件处理程序会先执行,element2后执行。

事件冒泡
但你使用事件冒泡的时候

/ \ 
---------------| |----------------- 
| element1 | | | 
| ---------- -| |----------- | 
| |element2 | | | | 
| ------------------------- | 
| Event BUBBLING | 
-----------------------------------

element2的事件处理程序会先执行,element1的事件处理程序后执行。

W3C模式
W3C决定在这场战争中保持重力。在W3C事件模型中任何事件发生都是首先被捕获直到到达目标元素,然后再冒泡。

| | / \ 
-----------------| |--| |----------------- 
| element1 | | | | | 
| ----------- --| |--| |----------- | 
| |element2 \ / | | | | 
| -------------------------------- | 
| W3C event model | 
------------------------------------------

作为设计师的你,可以随意选择把事件处理程序注册在捕获还是冒泡阶段。通过之前高级模式里面介绍的addEventListener()方法就可以完成。如果最后一个参数是true那么就设置成为事件捕获,如果是false就设置为事件冒泡。

假设你这样写
element1.addEventListener('click',doSomething2,true)
element2.addEventListener('click',doSomething,false)
如果用户在element2上单击就会发生下面的事情:
、click事件发生在捕获阶段。这样看来,如果element2的任何一个父元素有onclick事件处理程序那么都会执行。
、事件在element1上发现了doSomething2(),那么就会执行它。
、事件向下传递直到目标本身,再没有其他的捕获阶段程序了。事件转而进入冒泡阶段然后就会执行doSomething(),也就是element2注册在冒泡阶段的事件处理程序。
、事件再向上传递再检查是否有父元素在冒泡阶段设置事件处理程序。这里没有,所以什么也不会发生。
反过来:
element1.addEventListener('click',doSomething2,false)
element2.addEventListener('click',doSomething,false)

现在如果用户在element2上面点击就会发生:
、事件click发生在捕获阶段。事件会查找element2的父元素是否有在捕获阶段注册事件处理程序,在这里没有。
、事件向下传递直到目标本身。然后开始冒泡阶段,执行dosomething(),这个是注册在element2冒泡阶段的事件处理程序。
、事件继续向上传递然后检查是否有父元素在冒泡阶段注册了事件处理程序。
、事件发现了element1.然后doSomething2()就被执行了。

传统模式下的兼容性
对于那些支持W3C DOM的浏览器来说,传统的事件注册

element1.onclick = doSomething2;
就被看做是注册在冒泡阶段的。

事件冒泡的使用
很少有设计师意识到事件捕获或者冒泡。在网页制作的今天,貌似没必要让一个冒泡事件被一系列的事件处理程序来处理。用户也会在单击之后发生一系列事件而感到迷惑,通常你也想让你的事件处理程序的代码保持一定的独立性。当用户点击一个元素,发生了一些事情,当他单击其他元素,那么其他再发生其他事情。
当然在将来也许会改变,最好让模式向前兼容。但是如今最实用的事件捕获和冒泡就是默认函数的注册。

它总是会发生
首先你需要理解的就是事件捕获或者冒泡总是在发生的。如果你为你的整个页面定义了一个onclick事件:

document.onclick = doSomething; 
if (document.captureEvents) document.captureEvents(Event.CLICK);

你在任意元素上的click时间都会冒泡到页面然后出发了这个事件处理程序。只有当前面的事件处理程序明确的阻止冒泡,才不会传递到整个页面。

使用
因为每个事件都会在整个文档上停止,默认的事件处理程序就变得可能。假设你有一个这样的页面:

------------------------------------ 
| document | 
| --------------- ------------ | 
| | element1 | | element2 | | 
| --------------- ------------ | ------------------------------------ 
element1.onclick = doSomething; 
element2.onclick = doSomething; 
document.onclick = defaultFunction;

现在如果用户点击了element1或者element2那么doSomething()就会执行。在这如果你愿意也可以阻止他的传播。如果不的话,那么defaultFunction()就会执行。用户在其他地方的点击也会让defaultFunction()执行。有时候这可能有用。
设置全局的事件处理程序在写拖动代码的时候就很有必要。通常一个层上的mousedow事件会选择这个层然后对mousemove事件做出回应。虽然mousedown通常注册在这个层上来避免一些浏览器的bug,但是其他的事件处理程序都必须是全局的(document-wide)。
记住浏览器逻辑的第一定律:什么都会发生的,而且经常是在你做的准备最少的时候。可能发生的是用户的鼠标疯狂的移动然后代码没有跟上导致鼠标已经不再这个层上了。
如果在某个层上注册了onmousemove事件处理程序,如果这个层不再响应鼠标的移动了,那么肯定会让用户感到迷惑。
如果某个曾上注册了onmouseup事件处理程序,那么程序会在用户松开鼠标的时候程序没有捕捉到造成这个层还在随着鼠标移动。
在这种情况下事件冒泡就很重要,因为全局的事件处理程序会保证执行的。

关掉它
但是通常你想关闭所有的相关的捕获和冒泡。另外,如果你的文档结构非常的复杂(比如一大堆复杂的表格之类)你也需要关闭冒泡来节省系统资源。要不然浏览器就得一个个的查看父元素是否有事件处理程序。虽然可能一个都没有,但是查找一样浪费时间。
在微软模式里你必须讲事件的cancelBubble属性设置为true。
window.event.cancelBubble = true
在W3C模式中你必须调用stopPropagation()方法。

e.stopPropagation()
这会阻止这个事件的冒泡阶段。阻止事件的捕获极端基本上是不可能的。我也想知道为啥。
一个完整的跨浏览器的代码如下:

function doSomething(e) 
{ 
if (!e) var e = window.event; 
e.cancelBubble = true; 
if (e.stopPropagation) e.stopPropagation(); 
}

在不支持cancelBubble的浏览器里面设置也不会有啥问题。浏览器会创建一个这样的属性。当然是没啥用的,只是为了安全。

currentTarget
我们之前讲过,一个事件包含target或者srcElement包含一个发生事件的元素的引用。在我们的例子是element2,因为用户点击了他。
理解在捕获和冒泡过程中这个target是不会改变的非常重要:他一直指向element2.
但是假设我们注册了下面的事件处理程序:

element1.onclick = doSomething;
element2.onclick = doSomething;
如果用户点击element2那么doSomething()执行了两次。那么你怎样知道那个HTML元素处理着这个事件呢?target/scrElement也不能给出答案,从事件一开始就一直指向element2.
为了解决这个问题W3C添加了currentTarget属性。它包含一个正在处理的事件的HTML元素的引用:就是我们想要的那个。不幸的是微软模式没有类似的属性。
你也可以用this关键字。在这个例子里就是指向正在处理的事件的HTML元素,就先currentTarget。

微软模式的问题
但是当你使用微软的事件注册模型,this关键字不是只想HTML元素的。然后又没有一个像currentTarget类似的属性,这就意味着如果你这么做:

element1.attachEvent('onclick',doSomething)
element2.attachEvent('onclick',doSomething)
你就不知道那个HTML元素正在处理事件。这是微软的事件注册模式最严重的问题所以就根本不要用他,即使是那些只在IE/win下的程序。
我希望微软能够尽快添加一个类似currentTarget的属性或者遵循标准?我们设计急需啊。

继续
如果你想继续学习,请看下一章。
原文地址:http://www.quirksmode.org/js/events_order.html
我的twitter: @rehawk

Javascript 相关文章推荐
javascript实现的listview效果
Apr 28 Javascript
Jquery多选下拉列表插件jquery multiselect功能介绍及使用
May 24 Javascript
JS解决ie6下png透明的方法实例
Aug 02 Javascript
jquery插件orbit.js实现图片折叠轮换特效
Apr 14 Javascript
简要了解jQuery移动web开发的响应式布局设计
Dec 04 Javascript
JavaScript数值千分位格式化的两种简单实现方法
Aug 01 Javascript
angularJs中ng-model-options设置数据同步的方法
Sep 30 Javascript
js中innerText/textContent和innerHTML与target和currentTarget的区别
Jan 21 Javascript
layui(1.0.9)文件上传upload,前后端的实例代码
Sep 26 Javascript
基于VUE的v-charts的曲线显示功能
Oct 01 Javascript
CountUp.js实现数字滚动增值效果
Oct 17 Javascript
JavaScript创建表格的方法
Apr 13 Javascript
JavaScript Event学习第七章 事件属性
Feb 07 #Javascript
JavaScript Event学习第六章 事件的访问
Feb 07 #Javascript
JavaScript Event学习第五章 高级事件注册模型
Feb 07 #Javascript
JavaScript Event学习第四章 传统的事件注册模型
Feb 07 #Javascript
JavaScript Event学习第三章 早期的事件处理程序
Feb 07 #Javascript
JavaScript Event学习第二章 Event浏览器兼容性
Feb 07 #Javascript
JavaScript Event事件学习第一章 Event介绍
Feb 07 #Javascript
You might like
解决php 处理 form 表单提交多个 name 属性值相同的 input 标签问题
2017/05/11 PHP
php-beanstalkd消息队列类实例分享
2017/07/19 PHP
会自动逐行上升的文本框
2006/06/30 Javascript
JQuery 选择器、过滤器介绍
2011/02/14 Javascript
JavaScript call apply使用 JavaScript对象的方法绑定到DOM事件后this指向问题
2011/09/28 Javascript
from 表单提交返回值用post或者是get方法实现
2013/08/21 Javascript
瀑布流布局代码一例
2014/04/11 Javascript
超漂亮的Bootstrap 富文本编辑器summernote
2016/04/05 Javascript
js获取元素的标签名实现方法
2016/10/08 Javascript
Angular 4环境准备与Angular cli创建项目详解
2017/05/27 Javascript
nodejs项目windows下开机自启动的方法
2017/11/22 NodeJs
JavaScript代码实现txt文件的上传预览功能
2018/03/27 Javascript
vue2.0 + ele的循环表单及验证字段方法
2018/09/18 Javascript
你不可不知的Vue.js列表渲染详解
2019/10/01 Javascript
小程序表单认证布局及验证详解
2020/06/19 Javascript
微信小程序视频弹幕发送功能的实现
2020/12/28 Javascript
[05:06]TI4西雅图DOTA2前线报道 海涛密探LGD训练
2014/07/09 DOTA
Python实现压缩与解压gzip大文件的方法
2016/09/18 Python
Python如何实现MySQL实例初始化详解
2017/11/06 Python
Python数据结构与算法之完全树与最小堆实例
2017/12/13 Python
在Python 2.7即将停止支持时,我们为你带来了一份python 3.x迁移指南
2018/01/30 Python
python利用微信公众号实现报警功能
2018/06/10 Python
Python3连接SQLServer、Oracle、MySql的方法
2018/06/28 Python
10 行Python 代码实现 AI 目标检测技术【推荐】
2019/06/14 Python
Python中有几个关键字
2020/06/04 Python
python rolling regression. 使用 Python 实现滚动回归操作
2020/06/08 Python
10个很棒的 CSS3 开发工具 推荐
2011/05/16 HTML / CSS
css3给背景图片加颜色遮罩的方法
2019/11/05 HTML / CSS
什么造成了Java里面的异常
2016/04/24 面试题
应急管理培训方案
2014/06/12 职场文书
班级标语大全
2014/06/21 职场文书
2014坚持党风廉政建设思想汇报
2014/09/18 职场文书
校园会短篇的广播稿
2014/10/21 职场文书
先进典型事迹材料
2014/12/29 职场文书
工程资料员岗位职责
2015/04/13 职场文书
创业开店,这样方式更合理
2019/08/26 职场文书