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 相关文章推荐
用js实现trim()的解决办法
Apr 16 Javascript
javascript中对Attr(dom中属性)的操作示例讲解
Dec 02 Javascript
js获取当前页面的url网址信息
Jun 12 Javascript
jQuery模拟新浪微博首页滚动效果的方法
Mar 11 Javascript
总结JavaScript中布尔操作符||与&amp;&amp;的使用技巧
Nov 17 Javascript
javascript每日必学之条件分支
Feb 17 Javascript
jQuery实现图片局部放大镜效果
Mar 17 Javascript
微信小程序 swiper组件详解及实例代码
Oct 25 Javascript
jquery uploadify隐藏上传进度的实现方法
Feb 06 Javascript
Underscore之Array_动力节点Java学院整理
Jul 10 Javascript
vue 项目接口管理的实现
Jan 17 Javascript
vue父子模板传值问题解决方法案例分析
Feb 26 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
PHP5 安装方法
2007/01/15 PHP
php生成静态html页面的方法(2种方法)
2015/09/14 PHP
ThinkPHP实现分页功能
2017/04/28 PHP
File文件控件,选中文件(图片,flash,视频)即立即预览显示
2009/04/09 Javascript
jquery eval解析JSON中的注意点介绍
2013/08/23 Javascript
使用jquery局部刷新(jquery.load)从数据库取出数据
2014/01/22 Javascript
详解addEventListener的三个参数之useCapture
2015/03/16 Javascript
JavaScript学习笔记(三):JavaScript也有入口Main函数
2015/09/12 Javascript
jQuery版本升级踩坑大全
2016/01/12 Javascript
jQuery插件编写步骤详解
2016/06/03 Javascript
详解Bootstrap的iCheck插件checkbox和radio
2016/08/24 Javascript
Jquery循环截取字符串的方法(多出的字符串处理成&quot;...&quot;)
2016/11/28 Javascript
通过jsonp获取json数据实现AJAX跨域请求
2017/01/22 Javascript
bootstrap-Treeview实现级联勾选
2017/11/23 Javascript
基于vue2.0实现仿百度前端分页效果附实现代码
2018/10/30 Javascript
JavaScript 作用域实例分析
2019/10/02 Javascript
js中复选框的取值及赋值示例详解
2020/10/18 Javascript
[42:32]VP vs RNG 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.21.mp4
2020/07/19 DOTA
Python实现信用卡系统(支持购物、转账、存取钱)
2016/06/24 Python
python 不同方式读取文件速度不同的实例
2018/11/09 Python
Python常见的pandas用法demo示例
2019/03/16 Python
python selenium登录豆瓣网过程解析
2019/08/10 Python
Python文件操作函数用法实例详解
2019/12/24 Python
20行Python代码实现一款永久免费PDF编辑工具的实现
2020/08/27 Python
CSS3属性box-shadow使用指南
2014/12/09 HTML / CSS
Origins悦木之源英国官网:雅诗兰黛集团高端植物护肤品牌
2017/11/06 全球购物
函授本科毕业生自我鉴定
2013/10/16 职场文书
财务会计实习报告体会
2013/12/20 职场文书
《云雀的心愿》教学反思
2014/02/25 职场文书
大学学风建设方案
2014/05/04 职场文书
论文答谢词
2015/01/20 职场文书
云冈石窟导游词
2015/02/04 职场文书
2015年医务人员医德医风自我评价
2015/03/03 职场文书
创业计划书之小型广告公司
2019/10/22 职场文书
用Python进行栅格数据的分区统计和批量提取
2021/05/27 Python
JVM的类加载器和双亲委派模式你了解吗
2022/03/13 Java/Android