在开始的时候,需要在您的事件处理函数中定义一个参数变量,准备接收可能传入的事件对象。然后,通过简单的条件表达式把浏览器的事件对象赋值给上述的参数变量:7 J# v" x ~ c, w& q
function myFunc(evt) { evt = (evt) ? evt : ((window.event) ? window.event : "") // process event here }
% l9 ]5 g1 a3 A- B: O 如果事件对象真的以参数的形式传进来了,则在函数内部,事件对象就被保留在 evt 这个局部变量中。如果这个参数是 null,而且浏览器的 window 对象包含有一个 event 属性,则 window.event 对象就会把自己赋值给 evt 变量。( d) Q! {, W P3 D
然而,为了完成这个工作,还应该再包含一层或者多层条件控制,以便优雅地适应那些在事件模型中没有定义事件对象的的早期浏览器:: `* r4 W; [. j) V7 Z) K
function myFunc(evt) { evt = (evt) ? evt : ((window.event) ? window.event : "") if (evt) { // process event here } }
- q# H/ \/ i: Y( |! f: l9 T& r 为了把同样的方式应用到所有事件处理函数的构建中,您可以定义一个函数来兼容两种事件,即由绑定的标识属性显式传入的事件对象,以及由绑定的事件属性隐式传入的事件对象。这样即使您在开发过程中改变了事件绑定的风格,这个函数也不必改变。$ r' Q% V$ ~6 J6 V+ `5 A. @
瑞典自助餐式地选择事件对象$ R" _' a J+ n
然而,建立一个指向事件对象的引用只是战斗的一部分。来自不同事件模型的每一个事件对象都拥有自己的一套属性,以容纳事件的细节。下面的表格列出了最常用的属性,以及这些属性在上述三种事件对象类型中的名称。 ]4 Q# z. V7 J
表格 1. 流行的事件对象属性
2 W1 `, n) b# G6 P6 }2 B 描述 NN4 IE4+ W3C/Safari
' d7 I) M5 D2 f! m6 D4 Y* \3 | Event target target srcElement target7 ^: s# v P3 u! U: T
Event type type type type! q2 A# h ~, G* L* C8 y8 K
X coordinate on page pageX * pageX
$ H8 L( e4 f( q# @& h$ l& g Y coordinate on page pageY * pageY
. r' L) _$ ]. j# U1 C. ~# p/ n Mouse button which button button1 s ?3 a( a6 m
Keyboard key which keyCode keyCode7 R- V. p# m+ z' h" E, n
标注*的属性值可以通过对 event.clientX + document.body.scrollTop 或者 event.clientY + document.body.scrollTop 进行求值来得到。. a9 C9 I/ @) f' @- D$ s- {
Macintosh 版本的IE5在通常情况下都遵循 IE4+ 的事件对象模型,但是有一个例外,即 IE5/Mac 的事件对象既定义了 srcElement 属性,也定义了 target 属性,这两个属性都指向接收事件的元素。
& |0 g4 M m+ }# v+ l& u1 M: D 需要抽象的最重要的事件对象属性可能得算指向接收事件的 HTML 元素的引用。NN4 和 W3C 的事件对象采用相同的属性名(target),而 IE4+ 的事件对象则使用 srcElement 属性。这时候,对象检测技术(而不是费力劳神而又具有危险倾向的浏览器版本识别方法)再次拯救了我们。对于那些非文本容器的元素,一个简单的条件表达式就可以轻松处理脚本语法上的差别:/ R$ Y1 [$ s: u0 z
var elem = (evt.target) ? evt.target : evt.srcElement
/ N% J( j. u' U0 p0 K* w 从现在开始,您的脚本就可以读写任何浏览器对象模型公布出来的元素对象属性了。
( f$ e, E! A9 I* C u5 X+ z7 W W3C DOM结点的事件目标- Q( d$ z2 }& N: k
W3C DOM 的结点架构使得文档中的每一个结点都可以接收事件。在支持这一架构的浏览器中,发生在嵌套文本顶上的事件并不调用分配给文本容器的事件处理器,相应的文本结点才是该事件的目标结点。考虑如下场景:% {2 K3 Q6 t0 A" n$ k) l
在事件实例,当鼠标的指针在一个 SPAN 元素包含的文本顶上滚动时,该文本就会被高亮显示。 事件绑定的过程通过对象属性在 init() 函数中进行。从表面上看,当用户在 SPAN 元素顶上滚动鼠标时,onMouseOver 事件动作函数就为该元素指派一个与风格表单规则相关联的类名(highlight),该风格规则把文本的显示风格定义为粗体,黄色背景;而在 onMouseOut 函数中,则把风格恢复为原始的版本(类 normal)。请注意一个 toggleHighlight() 函数是如何在事件对象的 type 属性的帮助下,执行两个动作的(该属性在所有事件模型对象中的名称是相同的)。请试一下这个事件实例。
: T. I7 w! ]2 Y! P9 B 但是如果您把例子装载到 NN6,则鼠标事件的真正目标就是 SPAN 元素中的文本结点了。本文并不讨论事件的传播机制,但是请相信,W3C DOM 事件模型的缺省行为会使事件沿着结点的包含层次向上传播(和 IE4+ 中事件通过元素容器向上传播的机制很类似)。因此,在这个事件实例中。鼠标事件会从其真正的目标向上传递到文本结点的容器(也就是 SPAN 元素)。这些事件触发了 SPAN 元素中相应的事件处理器。
/ R4 r1 z' _0 d' `" }6 ^ 虽然事件处理器属于 SPAN 元素,事件对象还是保留文本对象的引用,并将它作为事件的原始目标。然而,只有对文本结点的容器进行动作,才能修改它的风格。为了实现 toggleHighlight() 函数的等价操作,使之可以修改SPAN容器的 className 属性,该函数需要派生出一个指向文本结点容器的引用。 T& ~9 J( F; v0 H( z' r5 P; j
一个策略是使用 W3C DOM 事件对象的 currentTarget 属性,该属性返回一个处理事件的结点的引用。脚本中的决策树需要考虑这个属性,增加代码之后的 toggleHighlight() 函数如下所示:, m! U1 T: o" V$ s
function toggleHighlight(evt) { evt = (evt) ? evt : ((window.event) ? window.event : "") if (evt) { var elem if (evt.target) { if (evt.currentTarget && (evt.currentTarget != evt.target)) { elem = evt.currentTarget } else { elem = evt.target } } else { elem = evt.srcElement } elem.className = (evt.type == "mouseover") ? "highlight" : "normal" } }- t) G0 E* [( }% Z' X3 M+ K! t2 e d
另一个可选的方法是考察由 target 属性返回的对象的 ronodeType 属性。一个能够把事件定向给文本结点的浏览器,也可以把一个文本结点的 nodeType 属性值报告为3,而不是报告为元素结点的类型(其值为1)。如果事件的目标是一个文本结点,则脚本程序就可以通过该文本结点的 parentNode 属性来得到其上级元素结点的引用。这种方法的决策树在某种程度上得到更多的改进:
) R6 z, R. g) {! i2 ^( t) D2 U function toggleHighlight(evt) { evt = (evt) ? evt : ((window.event) ? window.event : "") if (evt) { var elem if (evt.target) { elem = (evt.target.nodeType == 3) ? evt.target.parentNode : evt.target } else { elem = evt.srcElement } elem.className = (evt.type == "mouseover") ? "highlight" : "normal" } }
6 w& V1 F8 y! [- B' v" ^3 ] 如果您正在用遵循 W3 的浏览器阅读本文,则请尝试这个修改过的版本,看看鼠标滚动时的风格变化。
! Y( o+ G$ z6 g( y @# Y/ i0 l: \ 这个页面使用了嵌入到事件实例中的最新版本的 toggleHighlight() 函数,展示了如何使用 JavaScript 为那些能够显示期望效果的浏览器增加额外的价值,同时也可以那些基本的内容提供给仍然使用着较老版本或者不支持脚本编程的浏览器的用户,只不过在模式上不那么动人和便于交互。
$ U% Y$ Y+ k" q) A9 e2 M 一个事件处理函数的模板
3 D% B6 b0 n9 o& H 并不是每个事件处理函数都处理页面元素对象中同样的属性或者行为,但是,从上文的讨论可以派生出来的一个模板,您可以在这个模板的帮助下开始编码。模板如下: X, e+ `" }- x0 n2 h2 D: b1 K3 Y
function functionName(evt) { evt = (evt) ? evt : ((window.event) ? window.event : "") if (evt) { var elem if (evt.target) { elem = (evt.target.nodeType == 3) ? evt.target.parentNode : evt.target } else { elem = evt.srcElement } if (elem) { // process event here } } }
$ o4 O3 F. G& s0 ]$ h% w% R3 e 请把第一行的函数名替换为您希望的函数名,并在注视指示的地方开始书写具体事件的代码。这个格式应该可以为您提供一个起点,适合于您采用的任何跨浏览器的事件绑定风格。如果您需要在一个页面中多次使用这个格式,则可以进一步精简代码,即把读取目标的代码抽象成一个可重用的工具函数,然后在每一个事件处理函数中进行调用:* d* k- |+ D1 Q/ s* Z; [
// shared function function getTargetElement(evt) { var elem if (evt.target) { elem = (evt.target.nodeType == 3) ? evt.target.parentNode : evt.target } else { elem = evt.srcElement } return elem } function functionName(evt) { evt = (evt) ? evt : ((window.event) ? window.event : "") if (evt) { var elem = getTargetElement(evt) if (elem) { // process event here } } } |