javascript事件冒泡详解和捕获、阻止方法


Posted in Javascript onApril 12, 2014

一、事件的发生顺序

这个问题的起源非常简单,假设你在一个元素中又嵌套了另一个元素

-----------------------------------

| element1                        |

|   -------------------------     |

|   |element2               |     |

|   -------------------------     |

|                                 |

-----------------------------------

:并且两者都有一个onClick事件处理函数(event handler)。如果用户单击元素2,则元素1和元素2的单击事件都会被触发。但是哪一个事件先被触发?哪一个事件处理函数会被首先执行?换句话说,事件的发生顺序到底如何?

二、两种模型

不出所料,在那些“不堪回首”(浏览器大战)的日子里,Netscape和微软有两种截然不同的处理方法:

Netscape主张元素1的事件首先发生,这种事件发生顺序被称为捕获型
微软则保持元素2具有优先权,这种事件顺序被称为冒泡型
这两种事件顺序是截然相反的。Explorer浏览器只支持冒泡事件,Mozilla,Opera7和Konqueror两者都支持。而更古老的opera和iCab两者都不支持

三、捕获型事件

当你使用捕获型事件时

               | |

---------------| |-----------------

| element1     | |                |

|   -----------| |-----------     |

|   |element2  \ /          |     |

|   -------------------------     |

|        Event CAPTURING          |

-----------------------------------

:元素1的事件处理函数首先被触发,元素2的事件处理函数最后被触发

四、冒泡型事件

当你使用冒泡型事件时

               / \

---------------| |-----------------

| element1     | |                |

|   -----------| |-----------     |

|   |element2  | |          |     |

|   -------------------------     |

|        Event BUBBLING           |

-----------------------------------

:元素2 的处理函数首先被触发,元素1其次

五、W3C 模型

W3c明智的在这场争斗中选择了一个择中的方案。任何发生在w3c事件模型中的事件,首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段

                 | |  / \

-----------------| |--| |-----------------

| element1       | |  | |                |

|   -------------| |--| |-----------     |

|   |element2    \ /  | |          |     |

|   --------------------------------     |

|        W3C event model                 |

------------------------------------------

为一个web开发者,你可以选择是在捕获阶段还是冒泡阶段绑定事件处理函数,这是通过addEventListener()方法实现的,如果这个函数的最后一个参数是true,则在捕获阶段绑定函数,反之false,在冒泡阶段绑定函数。

假设你要做

element1.addEventListener('click',doSomething2,true)
element2.addEventListener('click',doSomething,false)

如果用户单击元素2,则接下来会发生:

(事件在这里就像一个观光客,由外至内游览,逐渐接近被触发的主要元素,然后又反向离开)

1.单击事件首先进入捕获阶段开始(逐渐接近元素2的方向)。查看元素2的祖先元素中是否有在捕获阶段有onclick处理函数的
2.发现元素1有一个,于是doSomething2被执行
3.事件检查到目标自己(元素2),捕获阶段没有发现更多的处理函数了。事件开始进入冒泡阶段,想当然执行doSomething(),这个绑定于元素2冒泡阶段的函数。
4.事件向远离元素2的方向,查看是否有任何祖先元素在冒泡阶段绑定了一个处理函数。没有这样的情况,所以什么也没有发生
相反的情况是:

element1.addEventListener('click',doSomething2,false)
element2.addEventListener('click',doSomething,false)

现在如果用户点击元素2会发生:

1.单击事件进入捕获阶段。查看元素2的祖先元素中是否有在捕获阶段有onclick处理函数的,结果一无所获
2.事件检查到目标自己。事件开始进入冒泡阶段,并且执行绑定于元素2冒泡阶段的函数。doSomething()
3.事件开始远离目标,检查元素2的祖先元素中是否有在冒泡阶段绑定了处理函数的
4.发现了一个,于是元素1的doSomething2()被执行

六、兼容性和传统模式

在支持w3c dom(文档对象模型) 的浏览器中,传统的事件绑定方法是

element1.onclick = doSomething2;

默认被视为在绑定于冒泡阶段

七、使用冒泡型事件

很少的开发人员会有意识的去使用冒泡型事件或者捕获型事件。在他们今天制作的网页中,没有必要让一个事件因为冒泡而被好几个函数处理。但是有时用户通常会很疑惑,因为在他们只点击了一次鼠标之后出现了许多种情况(多个函数被执行,因为冒泡)。而大多数情况下你还是希望你的处理函数相互独立的。当用户点击了某一个元素,发生什么,点击另一个元素,又对应发生些什么,相互独立,而不因为冒泡连锁。

八、一直在发生

首先你要明白的是事件捕获或者冒泡一直在发生。如果你给整个页面文档的定义一个通用onclick处理函数

document.onclick = doSomething;

if (document.captureEvents) document.captureEvents(Event.CLICK);

在页面上单击任何元素的单击事件,最终会冒泡至页面最高文档层,因此触发那个通用的处理函数,除非之前一个处理函数明确的指出终止冒泡,这样才冒泡才不会传播到整个文档层面

对上面代码第二句的补充:

>>>先说IE
object.setCapture() 当一个object的被 setCapture 后,他的方法将会被继承到整个文档进行捕获。
当不需要把方法继承到整个文档捕获时,要用 object.releaseCapture()
>>>others
Mozilla 也有类似的功能,方法稍微不同
window.captureEvents(Event.eventType)
window.releaseEvents(Event.eventType)
>>>example

//如果只有下面这句话,那只有点击obj是才会触发click    obj.onclick = function(){alert("something")}

//若加上下面这句话,则方法会被继承到document(或者window,不同浏览器不同)来捕获

obj.captureEvents(Event.click); //FF

obj.setCapture()  //IE

九、用法

因为任何事件传播终止于页面文档(这个最高层),这使默认的事件处理函数变得可能,假设你有这样一个页面

------------------------------------

| document                         |

|   ---------------  ------------  |

|   | element1    |  | element2 |  |

|   ---------------  ------------  |

|                                  |

------------------------------------

element1.onclick = doSomething;

element2.onclick = doSomething;

document.onclick = defaultFunction;

 

现在如果用户单击元素1或者元素2,doSomething()将被执行。如果你愿意的话,如果你不想让事件冒泡至执行defaultFunction(),你可以在这里阻止事件冒泡向上传播,。但是如果用户点击页面上的其他部位,defaultFunction()还是会被执行。这样的效果或许有时能用的上。

设置页面­——使处理函数有范围较大的触发面积,在“拖拽效果”脚本中是必须的。一般来说在某一个元素层上发生 mousedown事件意味着选择了这个元素,并且使它能够响应mousemove事件。虽然mousedown通常绑定于这个元素层上以避免浏览器bug,但是其他两者的事件函数的范围必须是整个页面(?)

记住浏览器学的第一法则(First Law of Browserology)是:一切皆有可能(anything can happen),并且是在你起码有点准备的时候。所以有可能发生的是,用户拖拽时,大幅度在页面上移动他的鼠标,脚本却不能在大幅度中做出反应,以至于鼠标也就不再停留在元素层上了

1.如果onmouseover处理函数绑定在元素层上,这个元素层不会再对鼠标的移动有任何反应,这会让用户觉得奇怪
2.如果onmouseup处理函数绑定在元素层上,事件也不能被触发,后果是,用户想放下这个元素层后,元素层持续对鼠标移动做出反应。这会引起(用户)更多的迷惑(?)

所以在这个例子中,事件冒泡非常的有用,因为将你的处理函数放在页面层能保证他们一直能被执行

十、把它给关了(阻止事件冒泡)

但是一般情况下,你会想关了所有的冒泡和捕获以保证函数之间不会打扰到对方。除此之外,如果你的文档结构相当的复杂(许多table之间相互嵌套或者诸如此类),你也会为了节省系统资源,而关闭冒泡。此时浏览器不得不检查目标元素的每一个祖先,看是否它有一个处理函数。即使一个都没有找到,刚刚的搜索同样花费不少时间

在微软的模型中,你必须设置事件的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属性的浏览器中设置cancelBubble无伤大雅。浏览器会耸一耸肩然后创造一个这个属性。当然这也并不能真正的取消冒泡,但至少能保证这条命令是安全正确的

十一、currentTarget

像我们之前看到的一样,一个事件用target或者是srcElement属性用来表示事件究竟发生在哪个目标元素上(即用户最初点击的元素)。在我们的例子中是元素2,因为我们单击了它。

非常重要的是,要明白在捕获或者冒泡阶段的目标元素是不变的,它始终与元素2相关联。

但是假设我们绑定了以下函数

element1.onclick = doSomething;
element2.onclick = doSomething;

如果用户单击元素2, doSomething()会被执行两次。但是你怎么知道哪个html元素正在响应这个事件?target/srcElement也没有给出线索,但人们总是更倾向于元素2,因为它是引起事件的原因(因为用户点击的是它)。
为了解决这个问题,w3c 增加了currentTarget这个属性,它就指向正在处理事件的元素:这恰是我们需要的。很不幸的是微软模型中并没有相似的属性
你也可以使用”this”关键字。在上面的例子中,它相当于正在处理事件的html元素,就像currentTarget。

十二、微软模型的问题

但是当你使用微软事件绑定模型时,this关键字并不相当于HTML元素。联想缺少类似currentTarget属性的微软模型(?)——按上面的代码操作的话,你这么做便意味着:

element1.attachEvent('onclick',doSomething)
element2.attachEvent('onclick',doSomething)

你无法确切知道哪一个HTML元素正在负责处理事件,这是微软事件绑定模型最严重的问题,对我来说,这也是我从不使用它的原因,哪怕是在开发仅供Windows下的IE的应用程序

我希望能够尽快增加currentTarget类似的属性——或者遵循标准?web开发者们需要这些信息

后记:

因为没有实战过javascript,所以这篇文章有些地方我并不是很理解,只能硬生的翻译出来,比如谈拖拽效果的那一段,如果大家有什么补充和疑问可以留言给我,谢谢支持啦!

 PS:这里再为大家推荐一款关于JS事件的在线查询工具,归纳总结了JS常用的事件类型与函数功能:

javascript事件与功能说明大全:

Javascript 相关文章推荐
关于jquery input textare 事件绑定及用法学习
Apr 03 Javascript
Firefox和IE兼容性问题及解决方法总结
Oct 08 Javascript
js鼠标滑轮滚动事件绑定的简单实例(兼容主流浏览器)
Jan 14 Javascript
安装使用Mongoose配合Node.js操作MongoDB的基础教程
Mar 01 Javascript
页面向下滚动ajax获取数据的实现方法(兼容手机)
May 24 Javascript
jQuery插件cxSelect多级联动下拉菜单实例解析
Jun 24 Javascript
HTML Table 空白单元格补全的简单实现
Oct 13 Javascript
利用Js+Css实现折纸动态导航效果实例源码
Jan 25 Javascript
Web开发使用Angular实现用户密码强度判别的方法
Sep 27 Javascript
vue.js-div滚动条隐藏但有滚动效果的实现方法
Mar 03 Javascript
详解使用create-react-app添加css modules、sasss和antd
Jul 31 Javascript
原生js实现随机点餐效果
Dec 10 Javascript
一个非常全面的javascript URL解析函数和分段URL解析方法
Apr 12 #Javascript
用jquery写的菜单从左往右滑动出现
Apr 11 #Javascript
瀑布流布局代码一例
Apr 11 #Javascript
javascript读写json示例
Apr 11 #Javascript
javascript如何判断输入的url是否正确
Apr 11 #Javascript
javascript判断office版本示例
Apr 11 #Javascript
JS、jquery实现几分钟前、几小时前、几天前等时间差显示效果的代码实例分享
Apr 11 #Javascript
You might like
先进的自动咖啡技术,真的可以取代咖啡师吗?
2021/03/06 冲泡冲煮
PHP默认安装产生系统漏洞
2006/10/09 PHP
深入php中var_dump方法的使用详解
2013/06/24 PHP
PHP统计nginx访问日志中的搜索引擎抓取404链接页面路径
2014/06/30 PHP
Ubuntu VPS中wordpress网站打开时提示”建立数据库连接错误”的解决办法
2016/11/03 PHP
javascript各浏览器中option元素的表现差异
2011/04/07 Javascript
js页面跳转的问题(跳转到父页面、最外层页面、本页面)
2013/08/14 Javascript
jquery序列化form表单使用ajax提交后处理返回的json数据
2014/03/03 Javascript
Vue.js每天必学之方法与事件处理器
2016/09/06 Javascript
jQuery插件FusionCharts绘制的3D双柱状图效果示例【附demo源码】
2017/04/20 jQuery
Webpack实现按需打包Lodash的几种方法详解
2017/05/08 Javascript
Vue之Watcher源码解析(2)
2017/07/19 Javascript
在vue项目创建的后初始化首次使用stylus安装方法分享
2018/01/25 Javascript
nodejs微信扫码支付功能实现
2018/02/17 NodeJs
详解VUE-地区选择器(V-Distpicker)组件使用心得
2018/05/07 Javascript
对angular2中的ngfor和ngif指令嵌套实例讲解
2018/09/12 Javascript
vue使用原生swiper代码实例
2020/02/05 Javascript
vue实现图片上传功能
2020/05/28 Javascript
Python作用域用法实例详解
2016/03/15 Python
Python实现FTP弱口令扫描器的方法示例
2019/01/31 Python
Python语言检测模块langid和langdetect的使用实例
2019/02/19 Python
Django实现单用户登录的方法示例
2019/03/28 Python
python3实现高效的端口扫描
2019/08/31 Python
numpy:找到指定元素的索引示例
2019/11/26 Python
python错误调试及单元文档测试过程解析
2019/12/19 Python
Win10用vscode打开anaconda环境中的python出错问题的解决
2020/05/25 Python
python是怎么被发明的
2020/06/15 Python
python中的错误如何查看
2020/07/08 Python
CSS3简单实现照片墙
2014/12/12 HTML / CSS
《盲人摸象》教学反思
2014/02/16 职场文书
工程管理英文求职信
2014/03/18 职场文书
关于中国梦的演讲稿
2014/04/23 职场文书
专科应届毕业生求职信
2014/06/04 职场文书
2014年保安个人工作总结
2014/11/13 职场文书
关爱留守儿童捐款倡议书
2015/04/27 职场文书
详细聊聊浏览器是如何看闭包的
2021/11/11 Javascript