[译]JavaScript事件(五)传统的事件注册模式

传统的事件注册模式

本页我将介绍对一个元素注册事件的最佳模式,这就是:当指定的元素上发生指定的事件,指定的脚本会执行。

在早期的浏览器中,注册只能通过内联模式(inline model)。由于DHTML从根本上改变了操作网页的模式,事件注册的模式就必须被扩展并且更加灵活。因此,浏览器场上推出了新的事件模型。网景已经在版本3开始,接着IE4也实现了。

因为网景3已经支持这种新模式,在浏览器大战前这已经成为事实上的标准。因此,这是微软最后一次被迫实现标准,以使其浏览器兼容无数的使用网景事件模式的网页。

所以这两种浏览器,事实上所有的现代浏览器都接受这行代码

element.onclick = doSomething;

这是注册事件处理的正确方法。每当HTML元素被用户单击,函数doSomething()被执行。因为它是被普遍支持的,因为它实际上是唯一一种跨浏览器支持的注册事件模式,理解他的可用性和限制非常重要。

由于这种模式出现的时候并没有正式标准,我称之为事件注册的传统模式。W3C已经标准化了事件注册,同时,微软还创建了一个高级模式(参考 Advanced models),但传统的模式仍工作正常。

高级事件注册

从Netscape 3/Explorer 4起,JavaScript 能识别产生事件的元素上的属性(property)。因此大多数HTML元素有了onclick, onmouseover, onkeypress等属性(property)。什么HTML元素有什么属性(property) —— 什么HTML元素支持什么事件 —— 取决于浏览器

这些属性(property)本身并不是新生事物,在最老的浏览器里已经存在:

<a href="somewhere.html" onclick="doSomething()">

这里A标签有一个onclick属性(attribute),在JavaScript中变成了A元素的属性(property)。在最古老的浏览器中事件处理程序只有通过页面源代码才能访问。所以如果你想在网页里每个连接都加上同一个事件,你只有在页面中硬编码,给每个A标签添加代码。

在传统模式中,HTML元素的onclick, onmouseover和其他所有事件属性(properties)都可以通过JavaScript访问。现在你可以添加,修改,删除事件处理程序,而不需修改丝毫HTML代码。当你从DOM访问到正确的HTML元素后,你可以把函数写到你选择的元素的属性(property)中,比如:

element.onclick = doSomething;

现在我们的示例函数doSomething()被注册到元素的onclick属性(property),当用户点击元素,就会执行。注意,事件名称必须全部小写

要删除事件处理程序,只要简单的设置onclick方法为空:

element.onclick = null;

事件处理程序也是普通的JavaScript方法。没有事件发生也可以执行。如果你

element.onclick()

doSomething()会立即被执行。你的函数期望一个事件,但是并没有真正的事件发生,所以它就不知道怎么做,并产生错误。因此这种执行方式很少用到。

微软增加了类似的fireEvent()方法到IE5.5+,语法是element.fireEvent('onclick') 

没有括号!

请注意,注册事件不要使用括号()。onclick方法期望一个完整的函数。如果你这样:

element.onclick = doSomething();

这个函数会(立即)执行,其结果将被注册到onclick。这不是我们想要的,我们希望事件发生时执行函数。此外事件处理函数一般都会期待一个事件,如果我们执行它但是上下文中并没有事件,就会得到严重的混乱,产生JavaScript错误。

我们应该把doSomething()整体复制到事件处理程序中。不立即执行,它应该在事件发生时运行。

this

在JavaScript中,this关键字总是指向一个函数的“拥有者”,在事件处理程序中,它总是指向处理事件的HTML元素,所以可以方便的访问它。

不幸的是,this关键字虽然很强大,但很难用,如果你并不确切是到他是如果工作的。我将在另一页讨论它。这里,我给出其在传统模式中使用的简短摘要。

在传统模式中this工作方式如下;请注意,与内联模式稍有不同,现在this关键字在函数中,而不是HTML的属性(attribute),不同之处将在其他页面里解释。

element.onclick = doSomething;
another_element.onclick = doSomething;
function doSomething() {
this.style.backgroundColor = '#cc0000';
}

如果你在任何HTML元素上注册了doSomething()作为click事件的处理函数。当用户点击时,该元素得到红色的背景。

匿名函数

假设你想让鼠标经过时改变所有DIV的背景颜色,鼠标移开时还原。要正确使用this,你可以这样:

var x = document.getElementsByTagName('DIV');
for (var i=0;i<x.length;i++) {
x[i].onmouseover = over;
x[i].onmouseout = out;
}
function over() {
this.style.backgroundColor='#cc0000'
}
function out() {
this.style.backgroundColor='#ffffff'
}

此代码将正常工作,没有问题。但由于over()out() 都很简单。把他们登记为匿名函数会更优雅。

...
for (var i=0;i<x.length;i++) {
x[i].onmouseover = function () {this.style.backgroundColor='#cc0000'}
x[i].onmouseout = function () {this.style.backgroundColor='#ffffff'}
}

onmouseover onmouseout 属性(properties)期望一个函数。我们不应该只拷贝 over()out(),而是在注册事件处理函数代码中立即定义一个处理函数。因为这些函数没有名字,所以叫匿名函数

这两种方式是一样的,唯一的区别是第种方式更整洁。我非常喜欢匿名函数,当我想注册一个简单的事件处理程序时就这样用的。

问题

传统模式的一个小缺点是onclick 属性(property)只能包含一个函数。当你想在一个事件注册多个事件处理程序时就有问题了。

例如,加入你想写一个模块,可拖放层。该模块注册一个onclick事件处理函数使单击启动拖放。你又写了一个模块,收集用户的点击跟踪信息,当onunload的时候静悄悄的发送到服务器,以便了解用户怎样使用你的页面。这个模块也注册到onclick事件上。

所以你真正想做的是: 

element.onclick = startDragDrop;
element.onclick = spyOnUser;

然而,事情在这里开始出错。第二个注册的函数会把第一个覆盖,当用户点击时只有spyOnUser执行。

解决方案当然是注册一个能执行这两个函数的函数。

element.onclick = function () {startDragDrop(); spyOnUser()}

解决注册问题

但是假设你不是在每个页面都使用这两个模块。现在你这样做:

element.onclick = function () {startDragDrop(); spyOnUser()}

你可能得到错误信息,因为两个功能之一可能没有定义。所以你要小心注册事件处理函数。当我们想注册 spyOnUser()startDragDrop() 可能会(也可能不会)注册,我们:

var old = (element.onclick) ? element.onclick : function () {};
element.onclick = function () {old(); spyOnUser()};

首先你定义一个变量old。如果元素目前已经有onclick处理函数,把这个旧的函数赋值给old,若没有,给old赋值一个空函数。现在给div重新注册事件处理函数,这个函数首先执行 old() 然后执行 spyOnUser()。 现在新的事件处理函数添加到元素上了,而以前注册的程序(如果有的话)将被保留。

最后一个问题:如果你想删除其中一个事件处理程序,但不是所有的怎么办?目前我不知道该如何做。你可能得用一些方法编辑element.onclick,但是我真的还没有研究过这个问题。

其他模式

因此,我们看到传统的事件处理模式使用简单,但想在一个元素上添加多个事件处理程序时会有讨厌的问题。W3C模式很好的解决了这个问题。

继续

如果你想顺序的读完本系列文章,你可以继续阅读 Advanced models

 

 全文目录:http://www.cnblogs.com/Kamal/articles/javascript_events_contents.html

原文链接:http://www.quirksmode.org/js/events_early.html

posted on 2010-02-02 15:39  PlayerYK  阅读(514)  评论(0编辑  收藏  举报

导航