JavaScript Event学习第四章 传统的事件注册模型

在最古老的JavaScript浏览器里注册事件只能通过内联模式。自从DHTML从根本上改变了你操作页面的方法,事件的注册就必须有扩展性而且要有很强的适应性。所以就必须有相应的事件模型。Netscape在第三代浏览器中就开始了,IE在第四代浏览器开始。
因为Netscape 3就开始支持这种新的事件注册模型,在浏览器战争前就是事实上的标准。所以微软不得不也是最后一次为了网上那些数不清的使用了Netscape事件处理模型的页面在兼容性上做出了让步。
所以这两个浏览器,事实上也是所有的浏览器都支持下面的代码:


代码如下:

element.onclick = doSomething;

这是注册一个事件的最好的办法。无论什么时候用户点击了这个HTML元素,那么doSomething()都会执行。这是唯一一个能够跨浏览的注册事件的最好的办法,深刻的理解这个模型和他的限制也是非常重要的。
因为没有官方的标准,所以我暂且称为传统事件注册模型(traditional event registration model)。同时,w3c也标准化了事件注册,微软也推出了高级模式,但是传统模式依然能很好的运行。

高级事件注册程序
从Netscape 3/IE 4开始,JavaScript能够识别元素上的一系列事件的属性。大多数HTML元素都有onclick,onmouseover,onkeypress等等属性。那些元素有哪些属性--哪些元素支持哪些事件--都依赖于浏览器。
这些属性对于他们本身也不是什么新颖的东西。在最古老的JavaScript浏览器里面就已经存在了。


代码如下:

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

这里的A标签就有一个onclick参数,在JavaScript里面就成为了A元素的属性。那些古老的浏览器的事件处理程序只能通过在页面源代码里面设置元素的参数这个办法来注册。如果你想让这个脚本在所有的A标签执行,那么你就需要再所有的链接上面加上onclick事件。
有了传统事件注册模型的到来,这些onclick,onmouseover或者HTML元素的其他事件处理就都可以通过JavaScript来注册了。现在你可以添加、修改或者删除一些事件处理程序而不用动HTML的一丝一毫。当你通过DOM来访问HTML元素的时候你就可以像下面这样写代码了:


代码如下:

element.onclick = doSomething;

现在我们的示例函数doSomething()就注册在了element元素的onclick属性上,而且当用户点击了这个元素函数就会执行。注意事件的名字必须都是小写。
删除这个事件处理程序,只要简单的让点击事件为空就行了:


代码如下:

element.onclick = null;

事件处理程序跟普通的JavaScript函数一样。即使事件没有发生的时候他也能执行。如果你则这样写:


代码如下:

element.onclick()

那么doSomething一样会执行。虽然如果是一个不知道做什么或者产生错误的函数,这也没有真实的事件发生。所以这是一种很少用来执行事件处理程序的方法。
微软的IE5.5和更高版本的IE还有一个fireEvent()方法来完成同样的事情。使用如下:


代码如下:

element.fireEvent('onclick')

没有括号
需要注意的是注册一个事件处理程序的时候你不能使用括号。onclick方法会被设置成为另外一个函数。如果你这样写


代码如下:

element.onclick = doSomething();

那么这个函数就会执行并且它的结果会被注册到onclick上。这可不是我们所期望的,我们只是希望在事件发生的时候函数能够执行。另外函数写出来是为了在事件发生的时候执行,如果没有关联的执行会造成严重的混乱和错误。
所以我们在事件处理程序中复制整个doSomething()方法。我们只是想在事件执行的时候执行这个函数。

this
在JavaScript里this关键字通常指函数的所有者。如果this指向事件发生的HTML元素,那么一切都是那么的美好,你可以很简单的做很多事情。
不幸的是,虽然this非常的强大,但是如果你不是明确的知道他怎么运作的话使用起来还是比较难的。关于这个我在另一个地方有详细的讨论,在这我在传统模式下做一些概述。
在传统模式里this工作如下;注意这个跟内联模式稍微有些不同。现在this关键字在函数里,而不是在HTML的参数上。这个区别后面会另外讲的。


代码如下:

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

如果你注册了doSomething()作为任何一个HTML元素的click事件,那么当用户点击那个元素的时候元素就得到一个背景。

匿名函数(Anonymous functions)
假设你想所有div在鼠标经过的时候改变背景色,然后在鼠标离开的时候返回背景色。正确的使用this,你可以这样写:


代码如下:

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

这些代码可以运行,没问题。但是既然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都是得到一个函数。与其拷贝over()和out(),不如直接定义一个事件处理程序在这个事件注册的脚本上。既然这些函数没有名字,那么他们就是匿名函数。
这两种注册事件处理程序的方法基本上一样,唯一的区别就是第二种的代码量少一些。我非常喜欢匿名函数并且我会在注册一个简单的事件处理程序的时候使用它。

问题
有一个小小的问题就是传统模式下onclick属性只能包含一个函数。当你想对一个事件注册多个事件处理程序的时候就有问题了。
比如,你已经写了一个可以拖动的模块。这个模块注册在onclick事件处理程序上所以当你点击它的时候就能开始拖动。你还写了一个模块可以跟踪用户的点击然后在onunload的时候发送信息给服务器,这样就能知道你的页面如何被使用的。这个模块也需要在元素上注册一个onclick事件。
所以事情可能会是这样:


代码如下:

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

这是就会发生错误。第二个注册程序会覆盖第一个,那么当用户点击元素的时候就只有spyOnUser()执行。
解决办法就是注册一个包含两个方法的方法:


代码如下:

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

灵活的注册
但是假设你没有在你网站的每个页面都使用两个模块。如果你还这样写:


代码如下:

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

你会得到一个错误信息因为其中有个函数是未定义的。所以在注册事件的时候要特别的小心。当我们在startDragDrop()可能已经注册的时候还想注册spyOnUser(),那么我们可以这样写:


代码如下:

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

首先你定义一个变量old。如果元素已经有了一个onclick的事件处理程序,那么就把它存入old,如果没有,就设置old为一个空的function。现在你要给一个div注册一个新的事件处理程序。那么程序就会首先执行old(),然后执行spyOnUser()。现在新的事件处理程序添加在了元素上,之前的注册过的(如果有)也被包含了。
最后一个问题:如果你想移除其中一个事件处理程序呢?现在我还不是很确定。你需要通过一些方法编辑element.onclick(),我还没有研究过这个问题。
其他模式
我们看到传统模式非常的简单易用,但是当你给一个事件添加几个程序的时候的解决办法还是比较丑陋的。W3C的事件处理程序很好的解决了这个问题。
继续
如果你想继续学习,请看下一章。

(0)

相关推荐

  • javascript 传统事件模型构造的事件监听器实现代码

    代码如下: 复制代码 代码如下: var br={}; br.eventRouter = {}; br.eventRouter.addListen = function(el,eventType,func){ if(!el.eventObjs){ el.eventObjs = {}; } if(!el.eventObjs[eventType]){ el.eventObjs[eventType]=[]; br.eventRouter.bindListen(el,eventType); } el.e

  • javascript中传统事件与现代事件

    大家都知道,IE中的现代事件绑定(attachEvent)与W3C标准的(addEventListener)相比存在很多问题, 例如:内存泄漏,重复添加事件并触发的时候是倒叙执行等. 下面是用传统事件的方法来处理封装事件的绑定: addEvent.ID = 1; // 事件计数器 function addEvent(obj, type, fn){ if(obj.addEventListener){ obj.addEventListener(type, fn, false); } else { /

  • JavaScript Event学习第四章 传统的事件注册模型

    在最古老的JavaScript浏览器里注册事件只能通过内联模式.自从DHTML从根本上改变了你操作页面的方法,事件的注册就必须有扩展性而且要有很强的适应性.所以就必须有相应的事件模型.Netscape在第三代浏览器中就开始了,IE在第四代浏览器开始. 因为Netscape 3就开始支持这种新的事件注册模型,在浏览器战争前就是事实上的标准.所以微软不得不也是最后一次为了网上那些数不清的使用了Netscape事件处理模型的页面在兼容性上做出了让步. 所以这两个浏览器,事实上也是所有的浏览器都支持下面

  • JavaScript Event学习第三章 早期的事件处理程序

    这些古老的浏览器只支持一种注册事件处理程序的方法,这个方法是Netscape发明的.因为Netscape先发制人,所以如果微软也想做支持JavaScript事件的浏览器就得跟着Netscape走,所以这里没有兼容性的问题.所以这种模式在任何支持JavaScript的浏览器都能运行---除了Mac上的IE3,他根本就不支持事件. 注册事件处理程序 在内联式的事件注册模型中,事件处理程序就像是一个HTML元素的属性,比如: <A HREF="somewhere.html" onCli

  • JavaScript Event学习第五章 高级事件注册模型

    W3C和微软都着力于发展自己的事件注册模型来取代Netscape的传统模型.虽然对于微软的模型我不是很感冒,但是w3c的还是不错的,除了这个鼠标定位 的问题.不过现在只有小部分浏览器支持. W3C W3C的DOM层面事件规范注意到了传统模式的问题.他对于你想在一个元素上绑定多个事件提供了一个很好的解决办法. W3C事件注册模型的关键就是addEventListener().你给他三个参数:事件类型,要执行的函数和一个布尔值(true或者false)我一会再解释.把我们熟知的doSomething

  • JavaScript Event学习第六章 事件的访问

    现在我们已经注册了事件处理程序,对于事件我们还想更深入的了解.我们想知道事件发生时候的鼠标位置,我们想知道用户按下了哪些键.这些都是可能的,虽然这部分有很多烦人的浏览器兼容性问题.(这里可以快速查看浏览器兼容性列表). 要读出事件的属性,必须要先能访问到事件. 浏览器兼容性 站在浏览器战争的角度看,Netscape实现了一个访问模型(后来被W3C做借鉴)和很多的事件属性,同时微软也做了同样的事情.当然这两种模型是完全不兼容的.但是就像我们再简介里面说的,如果 复制代码 代码如下: if (W3C

  • JavaScript Event学习第七章 事件属性

    当我们想去读一读关于Event的一些资料时,常常会湮没在大量的属性里面,这些属性其中的大多数不能良好的运行在大多数的浏览器.这里有event的兼容性列表. 我不打算给这些属性列个表,因为那些情况实在是太让人晕头了,而且对你的学习也不会有一点点的帮助.在写5段代码前我先要问关于浏览器的5个问题. 1.event的类型(type)是什么? 2.哪一个HTML元素是event的目标呢? 3.哪些键在event发生时被按下了? 4.哪个鼠标键在Event发生时被按下了? 5.在Event发生时鼠标的位置

  • JavaScript DOM学习第四章 getElementByTagNames

    getElementByTagNames(注意是复数的names)会获得一些tag的元素,然后按照他们的顺序保存在一个数组中.这非常的有用,比如在上一章的TOCScript中,就需要获得整个文章里面的所有的H3和H4. 我非常希望在node原型中加入这个功能,但是在IE和Safari里面不行.所以只能把他当做一个普通函数. 使用 getElementByTagNames有两个参数: 1.一个用逗号分隔的tag名称字符串. 2.一个可选的开始元素.如果存在则在该元素的子元素中查找这些tag,如果不

  • JavaScript Event学习第十一章 按键的检测

    第一个问题是对于按键事件来说根本就没有标准,按照规范说的:包含键盘等输入设备的事件模型会在以后的DOM规范中作出解释. 正如我们了解到的,浏览器在最开始设计的时候并没有一个标准,大家都像做实验似的,最后的成品虽然是有用的,但是肯定造成了兼容性的问题.按键问题也不例外:这里有两个属性能检测到用户按下了什么键,虽然有足够的理由为什么需要两个属性,但是依然并不是所有的浏览器都支持. 另外,在keypress与keydown和keyup之间还有一些很重要的区别. 最后就是windows和mac的区别了,

  • JavaScript Event学习第十章 一些可替换的事件对

    测试的局限性 这一章我们打算寻找哪些事件可以用来仿真鼠标事件.注意这一系列测试不包括屏幕阅读器.因为我不能满足所有的条件,因此测试也有局限性.这个测试目标仅仅只是那些在图形界面下的浏览器中不使用鼠标的用户. 我假设这些测试一样可以用来在一些移动设备上.因为条件不足,所以也不能测试.很多时候在移动设备上表现总是不尽如人意. 总结 不幸的是,我们不能制定一个严格的鼠标事件和非鼠标的一对一的方案,因为非鼠标事件和鼠标事件有很多不同.所以下面的建议在大多数场合适用,但不是所有. 下面就是我的测试结果:

  • JavaScript Event学习第八章 事件的顺序

    基本问题很简单.假设你的一个元素包含在另外一个元素中. 复制代码 代码如下: ----------------------------------- | element1 | | ------------------------- | | |element2 | | | ------------------------- | ----------------------------------- 这两个元素都有onclick事件处理程序.如果用户在element2上面单击那么在元素2和元素1上都

  • JavaScript Event学习第二章 Event浏览器兼容性

    在这里提出的事件,当他们发生在一个确定的HTML元素上的时候,他们的名字能够被大多数的浏览器所识别.也就是说,浏览器会查找你为这个HTML元素所注册的事件处理程序的脚本,而且会被立即执行.      一开始只有为数很少的一些事件.这些事件在几乎所有的JavaScript浏览器都能运行,即使是那些非常古老的.需要注意的是那些早期的事件只能工作在链接或者表单上,有时候也能运行在整个窗口上,但是其他的大多数HTML元素不行.      时代变迁,很多新的事件也给大家介绍过了.第四代浏览器和更高级的浏览

随机推荐