javascript事件冒泡详解和捕获、阻止方法 |
一、事件的发生顺序 这个问题的起源非常简单,假设你在一个元素中又嵌套了另一个元素 复制代码 代码如下: ----------------------------------- | element1 | | ------------------------- | | |element2 | | | ------------------------- | ----------------------------------- :并且两者都有一个onClick事件处理函数(event handler) 。如果用户单击元素2,则元素1和元素2的单击事件都会被触发 。但是哪一个事件先被触发?哪一个事件处理函数会被首先执行?换句话说,事件的发生顺序到底如何? 二、两种模型 不出所料,在那些“不堪回首”(浏览器大战)的日子里,Netscape和微软有两种截然不同的处理方法: Netscape主张元素1的事件首先发生,这种事件发生顺序被称为捕获型 三、捕获型事件 当你使用捕获型事件时 复制代码 代码如下: ---------------| |----------------- | 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处理函数的 复制代码 代码如下: element1.addEventListener(click,doSomething2,false) element2.addEventListener(click,doSomething,false) 现在如果用户点击元素2会发生: 1.单击事件进入捕获阶段 。查看元素2的祖先元素中是否有在捕获阶段有onclick处理函数的,结果一无所获 六、兼容性和传统模式 在支持w3c dom(文档对象模型) 的浏览器中,传统的事件绑定方法是 复制代码 代码如下: element1.onclick = doSomething2; 默认被视为在绑定于冒泡阶段 七、使用冒泡型事件 很少的开发人员会有意识的去使用冒泡型事件或者捕获型事件 。在他们今天制作的网页中,没有必要让一个事件因为冒泡而被好几个函数处理 。但是有时用户通常会很疑惑,因为在他们只点击了一次鼠标之后出现了许多种情况(多个函数被执行,因为冒泡) 。而大多数情况下你还是希望你的处理函数相互独立的 。当用户点击了某一个元素,发生什么,点击另一个元素,又对应发生些什么,相互独立,而不因为冒泡连锁 。 八、一直在发生 首先你要明白的是事件捕获或者冒泡一直在发生 。如果你给整个页面文档的定义一个通用onclick处理函数 复制代码 代码如下: document.onclick = doSomething; if (document.captureEvents) document.captureEvents(Event.CLICK); 在页面上单击任何元素的单击事件,最终会冒泡至页面最高文档层,因此触发那个通用的处理函数,除非之前一个处理函数明确的指出终止冒泡,这样才冒泡才不会传播到整个文档层面
>>>先说IE 复制代码 代码如下: //如果只有下面这句话,那只有点击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处理函数绑定在元素层上,这个元素层不会再对鼠标的移动有任何反应,这会让用户觉得奇怪 所以在这个例子中,事件冒泡非常的有用,因为将你的处理函数放在页面层能保证他们一直能被执行 十、把它给关了(阻止事件冒泡) 但是一般情况下,你会想关了所有的冒泡和捕获以保证函数之间不会打扰到对方 。除此之外,如果你的文档结构相当的复杂(许多table之间相互嵌套或者诸如此类),你也会为了节省系统资源,而关闭冒泡 。此时浏览器不得不检查目标元素的每一个祖先,看是否它有一个处理函数 。即使一个都没有找到,刚刚的搜索同样花费不少时间 在微软的模型中,你必须设置事件的cancelBubble的属性为true 复制代码 代码如下: window.event.cancelBubble = true 在w3c模型中你必须调用事件的stopPropagation()方法 复制代码 代码如下: e.stopPropagation() 这会阻止所有冒泡向外传播 。而作为跨浏览器解决方案应该这么作: 复制代码 代码如下: function doSomething(e) { 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,所以这篇文章有些地方我并不是很理解,只能硬生的翻译出来,比如谈拖拽效果的那一段,如果大家有什么补充和疑问可以留言给我,谢谢支持啦! |