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 相关文章推荐
图片之间的切换
Jun 26 Javascript
JS模块与命名空间的介绍
Mar 22 Javascript
JavaScript实现下拉列表框数据增加、删除、上下排序的方法
Aug 11 Javascript
jQuery动画效果相关方法实例分析
Dec 31 Javascript
第九章之路径分页标签与徽章组件
Apr 25 Javascript
微信小程序 九宫格实例代码
Jan 21 Javascript
详解javascript立即执行函数表达式IIFE
Feb 13 Javascript
Node.js中看JavaScript的引用
Apr 22 Javascript
jquery实现图片放大点击切换
Jun 06 jQuery
JavaScript使用atan2来绘制箭头和曲线的实例
Sep 14 Javascript
简单了解JavaScript arguement原理及作用
May 28 Javascript
实例讲解React 组件生命周期
Jul 08 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
浅谈使用 PHP 进行手机 APP 开发(API 接口开发)
2014/08/11 PHP
PHP中的类型提示(type hinting)功能介绍
2015/07/01 PHP
Yii框架分页技术实例分析
2019/08/30 PHP
经常用的图片在容器中的水平垂直居中实例
2007/06/10 Javascript
JavaScript对象、属性、事件手册集合方便查询
2010/07/04 Javascript
Javascript中Event属性搜集整理
2013/09/17 Javascript
js实现单行文本向上滚动效果实例代码
2013/11/28 Javascript
jQuery幻灯片带缩略图轮播效果代码分享
2015/08/17 Javascript
layui导航栏实现代码
2017/05/19 Javascript
JavaScript中一些特殊的字符运算
2017/08/17 Javascript
Vue实现自定义下拉菜单功能
2018/07/16 Javascript
vscode下的vue文件格式化问题
2018/11/28 Javascript
详解将微信小程序接口Promise化并使用async函数
2019/08/05 Javascript
基于Vue实现微前端的示例代码
2020/04/24 Javascript
vue项目,代码提交至码云,iconfont的用法说明
2020/07/30 Javascript
vant中的toast层级改变操作
2020/11/04 Javascript
[02:10]DOTA2亚洲邀请赛 EG战队出场宣传片
2015/02/07 DOTA
python实现rest请求api示例
2014/04/22 Python
python实现定制交互式命令行的方法
2014/07/03 Python
Python中的异常处理相关语句基础学习笔记
2016/07/11 Python
python导出chrome书签到markdown文件的实例代码
2017/12/27 Python
Python处理命令行参数模块optpars用法实例分析
2018/05/31 Python
python3 自动识别usb连接状态,即对usb重连的判断方法
2019/07/03 Python
django实现模板中的字符串文字和自动转义
2020/03/31 Python
webView加载html图片遇到的问题解决
2019/10/08 HTML / CSS
Hotter Shoes英国官网:英伦风格,舒适的鞋子
2017/12/28 全球购物
英超联赛的首选足球:Mitre足球
2019/05/06 全球购物
C&A巴西网上商店:时尚、衣服、手机和鞋子
2020/06/07 全球购物
Java提供了哪些企业应用编程接口
2015/02/13 面试题
民事诉讼授权委托书范文
2014/08/02 职场文书
德能勤绩廉个人总结
2015/02/14 职场文书
老人与海读书笔记
2015/06/26 职场文书
七年级数学教学反思
2016/02/17 职场文书
2016最新离婚协议书范本及程序
2016/03/18 职场文书
Oracle更换为MySQL遇到的问题及解决
2021/05/21 Oracle
PHP设计模式(观察者模式)
2021/07/07 PHP