没有共产党就没有新中国,没有 events 就没有 scripts。每个包含 JavaScript 的页面中: 几乎所有的脚本都是由 event 触发的。原因很简单。 JavaScript 可以增加交互性: user 做一些操作,页面给出响应。Events
是所有 JavaScript 应用的小心脏。本文介绍了事件处理机制,相关问题以及如何写出跨浏览器的 scripts。同时也展示了几个带有事件处理细节的页面。
因此JavaScript 检测用户的操作,以便知道何时做出反应,反应时执行哪个 functions 。
当用户做出某操作就会触发一个 event 。也有一些 events 不是由用户直接触发: 例如 load
event 是页面加载时触发的。
JavaScript 可以检测一些 events。从 Netscape 2 开始就可以把事件处理程序( event handler)附加到某个HTML 元素。 事件处理程序会等候某个 event (例如 链接上的 click 事件 )发生。这时候就会执行你定义好的JavaScript 代码来处理事件 。
用户操作会产生一个 event。当 script 对 event做出响应,这就是所谓的交互。
事件处理的历史(History of event handling)
假如没有事件处理机制,在页面中添加 JavaScript 就毫无意义。好的脚本可以对用户操作做出响应。因此网景发布第二版支持JavaScript的浏览器时,它也支持事件处理机制。
Netscape 模型
Netscape 2 只支持一些 events。Mouseover 和 mouseout 很快风靡,因为可以通过onMouseOver 改变 images ,通过onMouseOut
把它们变回去。在那个时候是很神奇的。也可以看到用户 submit 或 reset 表单,因此实现了客户端表单验证( form
validation)。浏览器也可以检测表单获得焦点、失去焦点、页面加载完成、开始上传等。尽管现在看来很稀松平常,但是在那个年代,这可是Web页面革命性的进展。真正的交互成为了可能,因为你可以对用户操作做出响应。
最古老的事件处理就下面一样:
<a href="somewhere.html" onclick="alert(‘I\‘ve been clicked!‘)">
这种最古老的事件处理机制实际上是由
Netscape 定义的标准。如果想要JavaScript工作,其他的浏览器包括IE都得遵从Netscape 2和3的处理事件的标准。因此这些最古老的事件和事件处理程序在任何JavaScript浏览器中都能很好地运行。
现代事件模型(Modern event models)
然而,自从引进这些简单的事件处理器后很多东西都发生了变化。首先事件数量增加,同时,事件注册机制也发生了变化。现在可以完全由JavaScript来设置。不再需要大量的附着于代码之上,你可以写一些很简单的代码来设置所有的事件处理程序。
第4版的浏览器还提供了更多的关于事件本身的信息。光标在哪儿(光标在撒钱)?事件何时触发?有没有键被按? 如果该界面元素存在父子元素,而且父子元素也定义了同样的事件,这个时候事件该如何处理呢,事件在父子元素之间是如何传递的呢,谁会先接收到这个事件,又是谁先处理呢?不同的浏览器其处理机制也不尽相同。
在网景和微软斗争的年代,两个公司选择了完全不同的事件模型。W3C非常理智的处理了这种差异,在两者之间采取了中和的方法 DOM
Event specification。除了一个严重漏洞( serious
flaw), W3C的模型, 添加了很多新功能并解决了旧模型的很多问题。
三个模型意味着事件处理机制在不同的浏览器中可能有所不同。
浏览器兼容性问题(Browser compatibility problems)
就像 DHTML,
W3C DOM 或其他的高级脚本技术一样,我们必须在浏览器中小心翼翼地执行代码。
在 Explorer 中调用 stopPropagation()
或者在 Netscape调用 srcElement
将会导致严重的错误甚至导致脚本不可用。因此在使用之前,我们必须确保浏览器支持该方法或该属性(
the methods or properties) 。
如下一个简单的代码分支
if (Netscape) { use Netscape model } else if (Explorer) { use Microsoft model }
只是一个近似的解决方案,因为它忽略了非主流的浏览器。现在的解决方案可以处理大量的 modern event handling,除非你的脚本机智地设定非主流浏览器不允许执行代码,因为它们不是 Netscape 或 Explorer。
所有其他非主流的浏览器都必须做出决定站在哪一队:支持哪一种模型。Konqueror/Safari, 选择了严格的标准并支持 W3C model。Opera and iCab 则比较谨慎,支持旧的 Netscape model 和 Microsoft model中的大部分。
但非主流的浏览器可能支持的是 Microsoft 标准,但实际上其事件属性既有 W3C 的也有 old Netscape model的。
不用使用浏览器检测(Don’t use that browser detect)
首先,千万不用使用 browser detect。这是最快的作死方法。任何代码如果使用navigator.userAgent来做事件模型的检测,那简直比没用还没用应该直接拉出去弹JJ。
其次,不要将 DHTML 对象检测(object
detection )与事件对象检测混为一谈。当书写DHTML 时我们通常这样检测是否支持DOM: if
is supported. 如果是酱紫,使用 Microsoft
(document.all)all
container 的脚本可以正常执行。
但 DHTML和事件处理程序有不同的浏览器兼容模式。例如: Opera 6 支持部分 W3C DOM 但不支持 W3C event model。因此在Opera中。所以代码使用 if
或者其他的事件模型检测都是不正确的。
(document.layers)
正确的提问(The right questions)
然后我们应该做些什么? 事件属性( event
properties)引起严重的问题。如果我们使用大量特定对象检测,可以解决99%的浏览器的不兼容问题。只有鼠标位置( mouse
position)非常的麻烦,其他的都比较简单了。
此外,最好是不去在意三个事件模型。相反,我们必须理解四个事件注册模型,两个事件访问模型和两个事件的命令。
事件处理和浏览器兼容性,参见事件兼容性表( event
compatibility tables)。
貌似很复杂,其实so easy。实际上,当我认识到要对不同事件模型单独处理之后,我才开始真正理解事件处理。 这都是关于如何提出正确的问题的。别这么问“如何写一个事件来处理脚本?” 这是个问题,但很难给出答案,你应该问得再具体一点,别人才好给出答案:
- “都有哪些事件?”
很多,当然在某些浏览器中,一些事件无法生效。
- “如何给一个HTML 元素注册事件处理程序?”
注册事件处理器有四种模型: 内敛模型( inline),
传统模型(traditional),
W3C模型和微软模型(W3C
and Microsoft)。第一种方法使用与所有浏览器,完全没有问题。 - “如何阻止事件的默认行为?”
从事件处理脚本中
return false
, 默认的动作就会被阻止。该技术标准是由 Netscape 2 定义的,现在依然有效。 - “若要获取更多事件相关的属性,该怎么做?”
有两种方法:W3C 或 Netscape and Microsoft。为了解决这个问题你还需要这样一行代码。
- “接下来改如何读取它的这些属性呢?”
要读取这些属性存在兼容性问题,这些问题在事件属性这篇文章中详细说明了。要解决兼容性问题还需要参考事件属性兼容性表 和进行一些严格的对象检测。
- “元素存在父元素,而且父元素也定义了同样的事件,这个时候事件该如何处理呢,事件在父子元素之间是如何传递的呢,谁会先接收到这个事件,又是谁先处理呢?” — 这个问题很有技术含量
不同的浏览器有不同的方法。有两个 事件处理顺序,事件捕获(event
capturing )和事件冒泡( event bubbling)。在实践中他们并不是很重要,只有少数情况下会用到,这种情况下我们通常会关心如何把它们关掉。需要两行代码。
All questions above will be treated on separate pages that give background information and the nuts and bolts of event handling.
The trick of writing cross–browser event handling scripts is not to use an overall event model check but to answer all these questions separately. You’ll find that you need to worry about browser compatibility mainly when reading out event properties.
First choose an event registration model, then make sure the event is accessed by all browsers, then read out the correct properties and then solve event order problems — if any occur. Thus you can solve each compatibility problem separately and ensure your
code runs in all browsers that support advanced event handling.
Continue
If you wish to go through all event pages in order, you should now continue with the events page.
Writing an event handling script
So how do you write an event handling script? On this page I give a quick overview for those who need fast answers and want to study the theory later.
Registering an event handler
The first step is registering your event handler. You have to make sure that the browser executes your script whenever the event you’ve chosen takes place.
There are four models for registering event handlers. inline, traditional, W3C
and Microsoft.
It’s best to use the traditional model, since it is completely cross–browser compatible and gives much freedom and versatility. To register an event handler, do
element.onclick = doSomething; if (element.captureEvents) element.captureEvents(Event.CLICK);
Now the function doSomething()
is registered as the handler of the click
event of HTML element element
.
This means that whenever the user clicks on the element, doSomething() is executed.
Accessing the event
When you’ve registered your event handler you start writing the actual script. Usually you want to access
the event itself, so you can read out information about the event.
To access the event so that you can read out its properties, always start your event handling function thus:
function doSomething(e) { if (!e) var e = window.event // e refers to the event }
Now e
refers to the event in all browsers and you can access the event.
Accessing the HTML element
Sometimes you also want to access the HTML element the event took place on. There are two ways for doing this: using the this
keyword or using thetarget/srcElement
properties.
The safest way to access the HTML element is by using the this
keyword. this
doesn’t always refer to the correct
HTML element, but in combination with the traditional model it works fine.
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 }
The target/srcElement
properties contain a reference to the HTML element the event originally took place on. Very useful, but when the event is
captured or bubbles up the target/srcElement
doesn’t change: it’s still the element the event originally took place on. (See the Event
properties page for target/srcElement
, see the this page
for the this
keyword)
Reading out properties
As to reading out interesting event properties,
this is the area with the worst browser incompatibilities. Study the event
compatibility tables and write your own script to read out the information you need.
Be sure to always use the most detailed object detection possible. First check if each property exists, then read out its value. For instance:
function doSomething(e) { if (!e) var e = window.event if (e.keyCode) code = e.keyCode; else if (e.which) code = e.which; }
Now code
contains the pressed key in all browsers.
Event order
Finally, you have to decide whether you want the events to bubble
up. If you don’t want that to happen, stop the propagation of the event.
function doSomething(e) { if (!e) var e = window.event // handle event e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); }
Writing the script
Now you can start actually writing the event handling script. Use the information the previous snippets of code give you to decide what actually happened when the event took place and how your script should react to it. Remember: keep the interaction logical
or your users won’t understand what’s happening.