`

浅谈Javascript中的事件流和事件绑定

 
阅读更多

事件流

浏览器中的事件流意味着页面上可有不仅一个,甚至多个元素响应同一个事件。而这一个或多个元素响应事件发生的先后顺序在各个浏览器(主要针对IE和Netscape)上是不同的。

冒泡型事件(Dubbed Bubbling)

IE上的解决方案就是冒泡型事件(Dubbed Bubbling)。冒泡型事件的基本思想是,事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

示例(1):点击我触发冒泡型事件流

示例(1)的XHTML代码结构:
<span id="cnt0">
<a href="#1" id="link0">点击我触发冒泡型事件流</a>
</span>

这个示例里我同时给span和a标签绑定了click事件,大家看到了,我们点击这个a标签,一次点击(click)同时触发了两个元素a和span的事 件。先触发的是绑定给a标签的click事件,然后触发的是span标签的click事件。也就是前面提到的“页面上可有不仅一个,甚至多个元素响应同一 个事件”和“事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发”。

这个示例里我们点击的第一目标是a标签这个链接,它就是前面提到的“最特定的事件目标”,然后才是span这个相对“不特定的事件目标”。再看看我的 XHTML代码结构,你会发现a标签包含在span标签中,也就是说span是a标签的父节点,如果大家还不是很清晰的知道他们之间的关系,让我们看看下 面的这个DOM树的结构图:



事件触发的顺序是从最特定的目标,沿着DOM树不断的向上触发click事件,就像气泡从下一直上冒的过程一样。“冒泡型”也就是这么得来的,也很形象。 这里要说明的是,由于我只给a和span绑定了click事件,所以“冒”到span就到顶了,如果你也给包含他们的p标签还有document绑定 click事件,这个冒泡的过程就会一直延续到document的事件触发才结束。

另外要说明的是,在IE5.5中冒泡的最高层DOM节点为document,IE6中则可以支持html节点。在Mozllia1.0及之后的版本也支持冒泡,而它则更可以冒到window窗口对象。

捕获型事件(Event Capturing)

相对IE4.0,Netscape4.0则使用的是捕获型事件的解决方案。这个事件触发的过程则正好和冒泡相反——在捕获型事件中,事件从最不精确的对象 (document对象)开始触发,然后到最精确的对象。还是前面的示例,不过现在换由捕获型事件触发(当然你需要在Netscape或Firefox中 测试)。

示例(2):点击我触发冒泡型事件流

示例(2)的XHTML代码结构:

复制内容到剪贴板
代码:
<span id="cnt1">
<a href="#1" id="link1">点击我触发冒泡型事件流</a>
</span>

事件触发的循序正好跟前面的冒泡相反,这里我就不赘述了。

DOM 事件流

这个事件流则是W3C制定一个标准规范,它同时支持两种事件流模式,不过是先发生捕获型事件流,再发生冒泡型事件流。

DOM事件流最独特的是,它支持文本节点也触发事件(IE中这不支持)。不过说实话,我现在还看不出来让文本节点支持事件有什么作用。

最后要说的是,根据最近大家在开发的实践过程中的运用,我们一般都采取冒泡型的事件流触发方式,这点我们的IE做的比较成功。至于原因,我想你可以通过上 面的解释可以看出,毕竟我们给元素触发事件,肯定是希望从我们最希望先触发(从最精确的)的那个开始。而DOM的先捕获后冒泡我觉得只能用让我很疑惑来形 容我的感受。因为如果按照DOM的事件流,我们的事件要被触发2次,而我们一般都只会选择一个类型的事件流,值希望触发一次,很难理解当初W3C是怎么想 的?!一个字——晕!可能我的理解能力有限,不过这是我的真是感受。

事件处理函数/监听函数

这里我不想做过多的介绍,我们知道在IE里使用attachEvent("NAME_OF_EVENT_HANDLER", fnHandler)给元素绑定事件,而在支持DOM事件流的浏览器里,则使用 addEventListener("NAME_OF_EVENT_HANDLER", fnHandler, isCapture)。前面我控制FIREFOX中触发捕获事件流,就是通过设置isCapture(ture:捕获;false:冒泡)做到的。还有就 是我们传统element.onclick或者element['on'+eventName],这个是所有浏览器都支持的事件绑定的监听器,而且我测试 的结果XP下的IE6~8、Firefox2.0~3.5、Safari4.0、Opera9.6.4、Chrome2.0.180都是以冒泡的事件流触 发的。更老的浏览器我就没有测试过了,不过根据《Pro Javascript Techniques》里介绍,老的浏览器使用onclick这样的事件绑定,触发的也是冒泡事件流。如果你有兴趣可以淘下那些古董浏览器,测试一番。不 过还是有不支持冒泡的事件的,下面我们就讲讲。

那些事件是支持冒泡,那些不支持?

这个是比较有意思的,这里的总结都来自PPK在YAHOO的演讲《Javascript Event 》(推 荐一定看看,很经典!),简单总结PPK的内容,基本上只有onload、unload、focus、blur、submit和change事件是不支持 冒泡的,这也是我在前面说“一般使用冒泡事件流”。自然向keydown、keypress、keyup、click、dbclick、 mousedown、mouseout、mouseover、mouseup、mousemove。用PPK的话说,那就是“Mouse and Key Events”支持冒泡,而Interface Events(也就是《Javascript高级程序设计》里的HTML(HTML是来构建interface的)事件。)则只支持捕获。

他又说了下是,click是“最安全的”事件,它即支持冒泡,又支持捕获。鼠标事件可以触发click,键盘事件也可以触发click。还有就是在支持 DOM事件的浏览器里,focus和blur是只支持捕获的,所以如果你如果用到我下面给出的addEvent函数,在给元素绑定focus或则blur 事件时,bCapture一定要设置为true。那么这里就发生了一个问题,IE是不支持捕获的,那不是触发不了这连个事件?呵呵,是个严重的问题哦?不 过在IE里使用focus和blur事件的时候,其实触发的是IE的focusin和focusout,当然这两个事件也是只支持冒泡的。

PPK虽然这么说,但是我还是想实践一下,于是我这里这么处理了下,window.onfocus = function(){alert('ok')},lnkOne.onfocus = demoClick;有趣的事情发生了!在IE6里,当你点击我的第一个示例链接,呵呵,视乎是即冒泡完了又捕获了,在IE8中则只冒泡了,不过这个只是 你点这个链接的时候发生的情况。接着我又算是试探性地“无意中”测试了下,点击其他的应用程序,又一个让我意想不到的情况发生了,居然先触发了 window.onfocus,接着触发了lnk.onfocus!!于是我立刻测试了IE6,一样!视乎IE也“疯狂”了一把,触发了onfocus的 捕获哦,哈哈!!!难道IE也支持捕获,IE也疯狂???!!!!还是这个现象有其他的解释??疑惑!?!?!!呵呵,起初我确实这么想,让我惊喜了一 把,不过仔细想了想,只是事件执行顺序的原因造成了这样的假象。

呵呵,原来在IE6里,点击链接,先触发了onfocus,弹出提示‘A’,然后关闭弹出的提示框窗口时,窗口又获得了焦点,又触发了window的焦点 事件。然后是触发了A标签的的click事件,然后关闭弹出的提示窗口时,又让窗口获得了焦点,然后又是A标签获得焦点。而IE8测试正确的,当触发了 click事件后,再关闭提示窗口的时候,就不在触发window的focus。哎!空欢喜了一场,我还以为windows的IE支捕获呢!!不过也不是 完全没有收获,如果你也像我这么整了,你要注意IE6会折腾两次的,不过只是在你点击的时候才会这样,如果用tab切换获得焦点,就只会触发A标签的 focus事件了。

还有在Firefox(我测试的最新的3.5)中,可千万别window.onfocus,不然你就挂了!很抱歉用FIREFOX看我这篇帖子的人,OK了几次后,你就挂了!!呵呵!!!(在BI浏览不会有问题,如果你访问下面的链接,不要用FF)

What is 'this'?

IE在前面给了我惊喜,this也给我惊喜。当然主要是我以前没有注意到,但是YAHOO的工程师们早以发现了。this这个关键字是根据上下文来决定 的,在我们的事件绑定的功能函数里,this应该是指向当前的元素节点对象,应该是一个Element对象。我想这个大家应该好理解:

示例代码:

复制内容到剪贴板
代码:
<span id="cnt0">
<a href="#1" id="link0">点击我触发冒泡型事件流</a>
</span>
<script type="text/javascript">
var link = document.getElementById('link1');
link.onclick = function(){
alert(this.tagName);
};
</script>

这段代码正式我在示例(1)中的处理方式,示例中this指向的a标签这个element对象,所以我们可以得到a标签的tagName这个属性‘A’。示例(2)里,我使用一个兼容的事件监听函数:

复制内容到剪贴板
代码:
function addListener(el, event, fn, bCapture){
var isCapture = bCapture ? bCapture : false;
try {
el.addEventListener(event, fn, isCapture);
}
catch (e) {
try {
el.attachEvent('on' + event, fn);
}
catch (e) {
el['on' + event] = fn;
}
}
}

在我们的支持DOM事件流的浏览器里,我们也可以得到正确的提示this.tagName为‘A’。this出现问题就在IE中,当我们使用attachEvent给元素绑定事件,现在你点击下面的链接:

示例(3):What is 'this'?

示例代码:

复制内容到剪贴板
代码:
<span id="cnt2">
<a href="#1" id="link2">What is 'this'?</a>
</span>
function whatIsThis(){
if (this === window) {
alert('This is a window object');
}
alert('So, This.tagName is:'+ '‘'+ this.tagName +'’。');
}
<script type="text/javascript">
var cntThree = document.getElementById('cnt2'), lnkThree = document.getElementById('link2');
addListener(lnkThree, 'click', whatIsThis);
addListener(cntThree, 'click', whatIsThis);
</script>

看清楚了吗?如果你在IE6~8中测试,那么你点的是window对象而不是一个a标签。晕倒!!!-_-! 所以你要小心,问题多多啊,要解决这个this关键字的问题,我给你的建议就是你可以考虑直接用传统的'onclick'或者修改下前面的绑定事件监听的函数:

修改后的代码:

复制内容到剪贴板
代码:
function addEvent(el, event, fn, obj, overrideContext, bCapture){
var context = el, isCapture = bCapture ? bCapture : false, wrappedFn = null;
if (overrideContext) {
if (overrideContext === true) {
context = obj;
}
else {
context = overrideContext;
}
}
wrappedFn = function(e){
var evt = e || event;
return fn.call(context, evt);
};
try {
el.addEventListener(event, wrappedFn, isCapture);
}
catch (e) {
try {
el.attachEvent('on' + event, wrappedFn);
}
catch (e) {
el['on' + event] = wrappedFn;
}
}
}

示例(4):再点点我,看我的‘this’是什么?

好了就这么多了,不知道对你有没有帮助,最后说明下,本文中的部分观点参考至《Javascript高级程序设计》(很好的一本书,推荐大家看看!),addEvent函数借鉴了YUI2.7的_addListener方法,这里也要谢谢YUI那些牛人,向他们致敬!

说明:由于这里发帖有些脚本无法运行,大家查看演示地址:http://www.yaohaixiao.com/my/event/index.html (请不要用FF浏览本页)

分享到:
评论

相关推荐

    浅谈JavaScript之事件绑定

    本文将深入探讨JavaScript事件绑定的概念,以及如何处理常见的问题和优化策略。 首先,我们来看一个基础的事件绑定示例。在给出的HTML结构中,有五个按钮,每个按钮都有一个`onclick`事件处理器。然而,当事件触发...

    浅谈JavaScript事件绑定的常用方法及其优缺点分析

    JavaScript事件绑定是网页交互的核心部分,它允许开发者在用户与页面元素交互时执行特定的代码。本文将探讨三种常见的JavaScript事件绑定方法:传统方式、W3C标准方式以及Internet Explorer (IE)方式,并分析它们...

    浅谈javascript面向对象编程

    ### 浅谈JavaScript面向对象编程 #### 一、概述 面向对象编程(OOP)是一种编程范式,它使用“对象”来设计软件。在JavaScript中,尽管语言本身并不支持传统的类(class)概念,但开发者可以通过原型继承和其他技术来...

    浅谈JavaScript语言中文档节点访问技术.pdf

    浅谈JavaScript语言中文档节点访问技术 在本文中,我们将探讨JavaScript语言中文档节点访问技术的相关知识点。节点访问是JavaScript语言中的一种基本技术,用于访问和操作HTML文档中的元素。下面是节点访问技术的...

    浅谈JavaScript 库———jQuery, ExtJs 的对比研究

    ### 浅谈JavaScript库——jQuery与ExtJs的对比研究 #### 一、引言 随着Web 2.0和RIA(Rich Internet Applications)技术的普及,JavaScript作为一种前端开发的重要工具,逐渐占据了重要的地位。它不再仅仅是用于...

    浅谈JavaScript的Polymer框架中的事件绑定

    在实践中,开发者应当根据项目的需求和个人偏好来选择事件绑定的策略。虽然Polymer提供了多种事件绑定方式,但出于性能和可维护性的考虑,推荐使用视图模型中定义的方法进行事件处理。这样做不仅使得代码结构更加...

    浅谈javascript中的call、apply、bind_.docx

    JavaScript 中的 call、apply、bind 方法详解 JavaScript 中的 call、apply、bind 方法是 Function 对象自带的三个方法,这三个方法的主要作用是转变函数中的 this 指向,从而可以达到“接花移木”的效果。下面将对...

    【JavaScript源代码】浅谈React双向数据绑定原理.docx

    在前端开发中,双向数据绑定是一种常见需求,它使得数据模型和视图之间的变化可以互相同步。然而,与 Vue.js 不同,React 并不原生支持双向数据绑定。在 Vue 中,双向数据绑定是通过 `Object.defineProperty`(在 ...

    浅谈js键盘事件全面控制

    今天,我们就来深入探讨JavaScript(js)中如何对键盘事件进行全面的控制,包括不同浏览器之间的兼容性处理以及代码实现和优化。 首先,我们需要明确浏览器中与键盘事件相关的主要类型,它们分别是keydown、...

    浅谈 javascript 事件处理

    在现代的JavaScript开发中,还常使用 addEventListener或attachEvent方法来为元素添加事件监听器,这样可以为同一个元素的同一个事件添加多个监听器,而不会覆盖之前绑定的事件处理程序。addEventListener方法还允许...

    浅谈javascript中this在事件中的应用

    本文将深入探讨`this`在JavaScript事件处理中的使用,以及它的指向如何根据不同的情况发生变化。 首先,`this`在函数中默认指向全局对象,这在浏览器环境中就是`window`对象。例如,如果我们有一个简单的函数`...

    浅谈javascript中createElement事件

    这个例子不仅演示了`createElement` 的基本用法,也涉及了事件处理器的绑定、DOM操作、数据类型转换和浏览器兼容性处理等关键知识点。 要特别注意的是,尽管`document.createElement` 是一种十分强大的工具,但在...

    浅谈关于JavaScript API设计的一些建议和准则

    JavaScript API设计是软件开发中的一项关键任务,它直接影响到其他开发者使用我们的代码库的便利性和效率。良好的API设计不仅可以简化开发者的使用过程,还可以提升整个应用的用户体验。以下是从给定文件内容中提炼...

    浅谈jQuery中的事件

    在JavaScript的世界里,jQuery库以其简洁的语法和强大的功能深受开发者喜爱,特别是在处理DOM操作和事件处理方面。本文将深入探讨jQuery中的事件处理,包括事件的绑定、合成事件、事件冒泡以及阻止事件等核心概念。 ...

    浅谈jQuery事件绑定原理

    当给dom用jq的方法绑定了事件,会生成对应的时间列表 可以看下面的例子(请在firefox中查看 因为firefox中对象支持toSource()) 代码如下: &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta http-equiv=”...

    浅谈JavaScript事件的属性列表

    JavaScript事件是编程中的一种机制,它允许我们对用户在网页上的交互作出响应。这些交互可以是点击、滚动、输入等。在HTML中,通过在元素上设置特定的属性,我们可以指定当这些事件发生时应执行的JavaScript代码。...

    浅谈jquery之on()绑定事件和off()解除绑定事件

    综上所述,on()和off()是jQuery中非常实用的API,它们使得事件绑定和解除变得简单和强大。正确地使用这些API,可以帮助开发者管理复杂的事件行为,并提高Web应用程序的性能和响应能力。在实际项目中,合理地运用事件...

Global site tag (gtag.js) - Google Analytics