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


Posted in Javascript onFebruary 07, 2010

在最古老的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的事件处理程序很好的解决了这个问题。
继续
如果你想继续学习,请看下一章。
Javascript 相关文章推荐
JavaScript 判断浏览器类型及版本
Feb 21 Javascript
javascript 年月日联动实现核心代码
Dec 21 Javascript
JavaScript为对象原型prototype添加属性的两种方式
Aug 01 Javascript
JavaScript高级程序设计 扩展--关于动态原型
Nov 09 Javascript
jQuery1.3.2 升级到jQuery1.4.4需要修改的地方
Jan 06 Javascript
jQuery基于ajax()使用serialize()提交form数据的方法
Dec 08 Javascript
Node.js项目中调用JavaScript的EJS模板库的方法
Mar 11 Javascript
AngularJS 基础ng-class-even指令用法
Aug 01 Javascript
详解js树形控件—zTree使用总结
Dec 28 Javascript
微信小程序动态添加分享数据
Jun 14 Javascript
一篇文章让你彻底弄懂JS的事件冒泡和事件捕获
Aug 14 Javascript
Vue发布订阅模式实现过程图解
Apr 30 Javascript
JavaScript Event学习第三章 早期的事件处理程序
Feb 07 #Javascript
JavaScript Event学习第二章 Event浏览器兼容性
Feb 07 #Javascript
JavaScript Event事件学习第一章 Event介绍
Feb 07 #Javascript
jQuery库与其他JS库冲突的解决办法
Feb 07 #Javascript
jQuery学习7 操作JavaScript对象和集合的函数
Feb 07 #Javascript
jQuery 学习6 操纵元素显示效果的函数
Feb 07 #Javascript
jQuery学习5 jQuery事件模型
Feb 07 #Javascript
You might like
一个改进的UBB类
2006/10/09 PHP
php学习笔记 数组遍历实现代码
2011/06/09 PHP
PHP请求Socket接口测试实例
2016/08/12 PHP
jquery load事件(callback/data)使用方法及注意事项
2013/02/06 Javascript
js动态设置div的值下例子
2013/10/29 Javascript
jquery中post方法用法实例
2014/10/21 Javascript
javascript正则表达式中的replace方法详解
2015/04/20 Javascript
jQuery获取某天的农历日期并判断是否除夕或新年的方法
2016/03/01 Javascript
jquery.cookie.js实现用户登录保存密码功能的方法
2016/04/15 Javascript
json格式的javascript对象用法分析
2016/07/04 Javascript
浅谈jquery.form.js的ajaxSubmit和ajaxForm的使用
2016/09/09 Javascript
用vue的双向绑定简单实现一个todo-list的示例代码
2017/08/03 Javascript
用vue快速开发app的脚手架工具
2018/06/11 Javascript
Vue 实现展开折叠效果的示例代码
2018/08/27 Javascript
Vue实现调节窗口大小时触发事件动态调节更新组件尺寸的方法
2018/09/15 Javascript
layui switch 开关监听 弹出确定状态转换的例子
2019/09/21 Javascript
JS实现网站楼层导航效果代码实例
2020/06/16 Javascript
[01:02:25]2014 DOTA2华西杯精英邀请赛 5 24 iG VS DK
2014/05/26 DOTA
修复CentOS7升级Python到3.6版本后yum不能正确使用的解决方法
2018/01/26 Python
使用Python向C语言的链接库传递数组、结构体、指针类型的数据
2019/01/29 Python
Python3 实现串口两进程同时读写
2019/06/12 Python
Django的性能优化实现解析
2019/07/30 Python
如何用Python来理一理红楼梦里的那些关系
2019/08/14 Python
python中property和setter装饰器用法
2019/12/19 Python
python序列类型种类详解
2020/02/26 Python
解决python中显示图片的plt.imshow plt.show()内存泄漏问题
2020/04/24 Python
Python垃圾回收机制三种实现方法
2020/04/27 Python
如何搭建pytorch环境的方法步骤
2020/05/06 Python
Python中的流程控制详解
2021/02/18 Python
HTML5 解决苹果手机不能自动播放音乐问题
2017/12/27 HTML / CSS
体育教师个人的自我评价
2014/02/16 职场文书
团日活动总结书
2014/05/08 职场文书
幼儿园园长安全责任书
2015/05/08 职场文书
2015年煤矿安全工作总结
2015/05/23 职场文书
培训简讯范文
2015/07/20 职场文书
世界十大动漫制作公司排行榜,迪士尼上榜,第二是美国代表性文化符
2022/03/18 欧美动漫