事件简介
事件(Event)是JavaScript应用跳动的心脏。本页我将简单介绍什么是事件处理,它的问题以及怎样写适当的跨浏览器脚本。我也会提供给你揭露event handling血淋淋的细节的页面。
没有事件就没有脚本,看看任何一个包含JavaScript的页面,几乎在所有的情况下,都是事件触发脚本。原因很简单,JavaScript的意义就是给页面加入互动性:用户做一些事情,页面做出反应。
因此JavaScript需要一种方法侦测用户的动作以知道该如何反应。还需要知道哪个函数去执行,这个函数就是你,网页的开发者,认为能增加你网页的吸引力的脚本。本文将描述编写这些脚本最佳的方法。这并不容易,但这是一件愉快的工作。
当用户做一些事情,事件(event)就会产生。也有一些事件不是直接由用户行为产生的: load 事件就是页面加载的时候触发的。
JavaScript可以侦测这些事件中的一些事件,从 Netscape 2开始,我们就可以附加事件处理程序(event handler)到特定的HTML元素上——早期只有链接和form。事件处理程序(event handler)等待,直到事件发生,比如点击一个链接,这时它通过执行已定义好的函数来处理(handles)这个事件(event)
事件处理程序的历史
就像我所说的,没有事件处理(event handling)网页上就没有插入JavaScript的点。最好的脚本就是对用户的动作做出相应的反应。因此当Netscape发布第二版浏览器的时候加入了JavaScript的支持,当然也支持事件(events)。
Netscape模型
Netscape 2 只支持几种事件。Mouseover 和 mouseout做出的图片切换效果很快使它成为传奇。 它还可以检测用户是否提交(submits)或者重置(resets)了一个Form,这使客户端验证成为可能。当页面加载(loading)完成或者开始卸载(unloading)的时候浏览器还可以检测form是否接受或者失去焦点。 虽然这写都是现代浏览器的基本标准,但在当时这是对网页革命性的扩展,因为可以对用户的动作做出反应,真正的交互才成为可能。
最古老的事件处理程序如下所示,当用户点击链接,事件处理程序执行并弹出警告。
<a href="somewhere.html" onclick="alert('I\'ve been clicked!')">
这种古老的添加事件处理程序的方法是由Netscape规范的,认识到这点非常重要。其他所有的浏览器包括微软的IE,想让JavaScript正常工作的话,都必须遵从Netscape 2和3的事件处理方法。因此这种古老的事件及事件处理程序能在所有支持JavaScript的浏览器中工作。
现代事件模型
然而,只后对事件处理的这种简单介绍发生了很大变化。首先事件(events)的数量增加了,其次注册事件处理程序(registering event handlers)到HTML的方法也改变了。现在你可以只用JavaScript,不再需要大段的事件处理程序杂乱的塞满你的代码。现在你可以写简单的脚本代码设置所有的事件处理程序(event handlers)。
第四代的浏览器还提供了事件的更多信息。鼠标在哪儿,事件什么时候发生的?有没有按键按下?最后浏览器厂商还得决定,当一个元素和它的父元素在相同事件上绑定了不同的处理程序时,怎样处理,哪个事件先触发?
因为这个功能是在浏览器战争时添加的,所以Netscape和Microsoft使用了截然不同的互不兼容的事件模型。最近W3C发布了DOM事件规范(DOM Event specification) ,出现了第三种事件模型。这种事件模型松散的建立在Netscape模型的基础上,虽然有一个严重的缺陷,但是它更全面,用途更广泛。 它是一个杰作,增加了许多新的有趣的功能,解决了旧的事件模型很多问题。当然了,既然存在三种模型,就意味着事件处理不能在所有浏览器里用同一种模型工作。
浏览器兼容问题
继续介绍。不像DHTML或者W3C DOM或者其他高级脚本技术。我们还得小心那些只能让特定浏览器理解的代码。在IE中调用stopPropagation()或者在Netscape调用
srcElement会发生可怕的错误,并且让你的脚本代码变得毫无用处。因此我们必须先检查浏览器是否支持我们想用的方法或者属性。
但是像这样简单的分支代码
use Netscape model
}
else if (Explorer) {
use Microsoft model
}
只是一个近似的解决方案,因为它落下了小众浏览器。最近期的方案应该可以处理相当数量的现代事件处理模型,除非你那智慧无穷的脚本决定如果浏览器不是Netscape 或者 Explorer,小众的浏览器直接禁止运行,试都不用试。
所有小众浏览器都面临一个艰难的抉择,支持哪种事件处理模型。Konqueror/Safar i始终选择最严格的标准,遵守和支持W3C模型,Opera 和 iCab 非常谨慎,支持旧的Netscape 模型也支持Microsoft的模型。我还没有研究过其他小小众的浏览器。
但是一个小小众的浏览器也可能用Microsoft 的方法访问事件,却用W3C 和 旧的Netscape模型获取事件属性。这应该不成问题,毕竟他们只是用自己的方式跟随著名的模型。 你的代码得为此做好准备。
不要使用浏览器检测
首先,绝对不要用浏览器检测。这是最糟糕的做法。 任何使用事件模型的脚本中,用了navigator.userAgent还不如不用,你应该对它敬而远之。
其次,不要混淆DHTML的对象检测(object detection)与事件对象检测(event object detection)。当我们写DHTML时,一般都检测DOM的兼容性。比如是否支持document.all,如果支持,就可以安全的使用微软的all容器。但是,DHTML和事件处理有不同的浏览器兼容模式。比如Opera 6部分支持W3C DOM,但不支持W3C的事件模型。因此,DHTML对象检测会让错误的代码分支在Opera中执行。所以如果在脚本中用(document.layers)等检测事件模型,也是不正确的。
正确的问题
那么,我们该怎么做呢?事件属性(event properties)的名称会导致很严重的问题。如果我们写使用大量的对象检测,可以解决99%的问题。只有获取鼠标位置(mouse position)有点难,其他的信息都很容易获取。
此外,最好不要想着去支持全部三种事件模型。相反,我们必须理解四种事件注册模型,两种访问模型和两种事件顺序。
可以先快速概览事件处理和浏览器兼容列表(event compatibility tables)
现在,这看起来非常复杂,但实际并非如此。当我发现这些的时候我才开始真正理解事件处理。要提出正确问题,不要问“我该怎样写事件处理脚本?”,虽然这是一个正确的问题,却很难去回答——我要写长达11页的答案。相反,应该问更具体的有具体答案的问题:
- “总共有多少事件?”
很多,当然有些事件不能在某些浏览器工作。 - “我该怎样注册事件处理程序到HTML元素?”
有四种方式:内联模式(inline), 传统模式(traditional), W3C和微软的模式(W3C and Microsoft)。第一种模式在所有浏览器中都能工作,没有任何问题。 - “我该怎样阻止事件的默认行为?”
如果你在事件处理脚本中return false,默认行为(default action)(比如打开链接,提交Form)就会被阻止。这项技术是由Netscape 2标准化,到现在仍然能正常使用。 - “我该如何访问事件,当我想获得更多信息时?”
有两种方法:W3C/Netscape 方法和 Microsoft方法。解决这两种方法的兼容问题只需要一行代码。 - “当我成功访问到事件时,我该怎样读出它的属性?”
这里就有兼容性问题了,就像我在事件属性(Event properties)这一页说的,你需要一个好用的事件兼容性列表(event property compatibility table),和一些严格的对象检测。 - “如果一个元素和他的祖先元素在同一个事件上有不同的事件处理程序,哪个会先触发?” — 好,我怀疑你已经问过这个问题了。
虽然各种不同的模型已经给出了答案,总共有两种事件顺序(event orders),事件捕获和事件冒泡。在日常情况下他们并不重要,但在罕见的情况下你会关心如何关闭他们。这需要两行代码。
所有上述问题都会在独立的页面给出背景资料和详细信息。
编写跨浏览器的事件处理程序的关键,并不是使用全面的事件模型检查,而是分别回答上面的问题,最后你会发现只需要担心读取事件属性的兼容性。
首先选择一种事件注册模型,然后保证浏览器能访问你需要的事件,然后读出正确的事件属性,然后解决事件顺序问题——如果你碰到的话。此时你就可以逐个解决兼容性问题,来确保你的脚本能在所有支持高级事件处理的浏览器中运行了。
继续
如果你想依次看完所有的事件,可以继续阅读事件页面。
写时间处理脚本
那么你怎样写事件处理脚本?对于那些需要一个快速的答案,然后再学理论的人,本页我先给出一个快速概观。
注册事件处理
第一步,注册你的事件处理程序。你要先确保事件发生时浏览器能执行你的脚本。
总共有四种注册事件的方式,内联模式(inline), 传统模式(traditional), W3C和微软的模式(W3C and Microsoft)。
最好用传统模式,它完全的跨浏览器支持,更全面,用途更广泛。要注册一个事件处理程序,这样
element.onclick = doSomething; if (element.captureEvents) element.captureEvents(Event.CLICK);
现在doSomething()这个函数被注册成HTML元素element的click事件的事件处理程序。这就是说,当element被点击时,doSomething()就会执行。
访问事件
当你注册好你的事件处理程序后,该开始写真正的脚本了。一般你会访问事件本身,来读取事件的详细信息。
访问事件获取它的属性,一般这样:
function doSomething(e) { if (!e) var e = window.event // e refers to the event }
现在e指向事件,你可以在所有的浏览器中访问它了。
访问HTML元素
有时候你想访问产生事件的HTML元素。有两种方法:使用this关键字或者使用target/srcElement属性。
最安全的做法是使用this关键字访问HTML元素。this关键字并不总是指向正确的HTML元素,但是它跟传统模式配合使用效果很好。
function doSomething(e) { if (!e) var e = window.event // e refers to the event // this refers to the HTML element which currently handles the event // target/srcElement refer to the HTML element the event originally took place on }
target/srcElement属性包含产生事件的原始元素。非常有用,但是如果事件被捕获或者冒泡
target/srcElement不会改变:还是指向产生事件的原始元素。(target/srcElement介绍参考事件属性页面,this关键字的介绍在这一页)
读取属性
至于读出有趣的事件属性,这部分有最严重的浏览器兼容问题。研究浏览器兼容性列表,然后写脚本读取你需要的信息。
一定要始终使用详细的对象检测。首先确认属性存在,然后读出他们的值,比如:
function doSomething(e) { if (!e) var e = window.event if (e.keyCode) code = e.keyCode; else if (e.which) code = e.which; }
到现在code在所有的浏览器中都能代表一个按键了。
事件顺序
最后,你要决定是否想要事件冒泡。如果不想,就停止事件的传递。
function doSomething(e) { if (!e) var e = window.event // handle event e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); }
写脚本
现在你可以开始写真正的事件处理脚本了,使用前面给出的代码片段的信息,去决定事件产生是发生了什么事,你的脚本怎样做出反应。记住:保持逻辑性,不然用户可能不理解发生了什么事。
全文目录:http://www.cnblogs.com/Kamal/articles/javascript_events_contents.html