[转载]生动详细解释javascript的冒泡和捕获,包懂包会

来源:互联网 发布:湖人拉塞尔大学数据 编辑:程序博客网 时间:2024/06/05 10:06

转载自:原文链接

前言:虽然精通jquery,但对它的原型javascript却不是很了解,最近在学习javascript中遇到了一些困难,比如冒泡和捕获,很多次被提到,但又不知究竟应用在何处。找到了一些好文章解惑,在这里分享给大家。

quirksmode的一系列文章都不错,通俗易懂,这篇只是一系列中的某一篇,有机会把javascript这系列都翻译给大家。

原文地址在这里http://www.quirksmode.org/js/events_order.html,句子中有标注“(?)”表示我对这个句子不是很理解,可能有误。正式开始:

事件的发生顺序

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

这里写图片描述

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

两种模型

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

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

捕获型事件

当你使用捕获型事件时
这里写图片描述
:元素1的事件处理函数首先被触发,元素2的事件处理函数最后被触发

冒泡型事件

当你使用冒泡型事件时

这里写图片描述
:元素2 的处理函数首先被触发,元素1其次

W3C 模型

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

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

假设你要做

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

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

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

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

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

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

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

兼容性和传统模式

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

element1.onclick = doSomething2;

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

使用冒泡型事件

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

一直在发生

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

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

//第二句话我也不知道什么意思,初学者,希望有能人能解释
在页面上单击任何元素的单击事件,最终会冒泡至页面最高文档层,因此触发那个通用的处理函数,除非之前一个处理函数明确的指出终止冒泡,这样才冒泡才不会传播到整个文档层面

用法(这一小节翻译的不好,因为没有实战,我也不是很理解,可以在留言中补充,我会更新)

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

这里写图片描述

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),并且是在你起码有点准备的时候。所以有可能发生的是,用户拖拽时,大幅度在页面上移动他的鼠标,脚本却不能在大幅度中做出反应,以至于鼠标也就不再停留在元素层上了

如果onmouseover处理函数绑定在元素层上,这个元素层不会再对鼠标的移动有任何反应,这会让用户觉得奇怪
如果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,所以这篇文章有些地方我并不是很理解,只能硬生的翻译出来,比如谈拖拽效果的那一段,如果大家有什么补充和疑问可以留言给我,谢谢支持啦!

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 感冒流浓鼻涕怎么办速效办法 孩子一直流清水鼻涕怎么办 宝宝鼻子呼噜呼噜响怎么办 鼻涕往嗓子里流怎么办 咳嗽痰多鼻涕多怎么办 没感冒嗓子痰多鼻涕怎么办 孩子感冒后鼻涕特别多怎么办 经常有鼻涕怎么办才好 怀孕后鼻涕痰多怎么办 鼻炎有鼻涕痰多怎么办 宝宝咳嗽痰多鼻涕多怎么办 宝宝两岁清鼻涕咳嗽痰多怎么办 喉咙咸咸的有痰怎么办 宝宝咳嗽鼻塞喉咙有痰怎么办 绝地求生刺激战场射击键误触怎么办 在皮卡堂卡的游泳了怎么办 假如遇到老赖没能力还钱怎么办 服刑人拒不执行伤害赔偿怎么办? 面对当前严峻形势作为军人怎么办 想起诉不知道对方地址怎么办 遇见家里来嫌疑人员怎么办 老滚5老婆死了怎么办 美化包安装之后闪退怎么办 蕉下的伞坏了怎么办 苹果7通话音质特别差怎么办 雨伞的伞骨坏了怎么办 雨伞的铁丝掉了怎么办 手机银行验证码忘了怎么办 应用安装验证码忘了怎么办 大王卡激活码找不到了怎么办 信用卡的激活码找不到怎么办 育碧账号忘了怎么办 uplay八折券丢了怎么办 不小心按到了育碧解绑怎么办 台式电脑连不上网怎么办 重装系统也安装不了cad怎么办 染发灰色偏绿了怎么办 vgm数据填错了怎么办 克里格插值 不符合正态分布怎么办 克里金插值无效的输出范围怎么办 穿完臭袜子要洗手吗不洗怎么办