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用Date对象处理时间实现思路及代码
Jan 31 Javascript
jCallout 轻松实现气泡提示功能
Sep 22 Javascript
深入解析JavaScript中的变量作用域
Dec 06 Javascript
ParseInt函数参数设置介绍
Jan 02 Javascript
AngularJS基础知识笔记之过滤器
May 10 Javascript
js获取图片宽高的方法
Nov 25 Javascript
Vue 单文件中的数据传递示例
Mar 21 Javascript
Bootstrap treeview实现动态加载数据并添加快捷搜索功能
Jan 07 Javascript
Vue下滚动到页面底部无限加载数据的示例代码
Apr 22 Javascript
JavaScript栈和队列相关操作与实现方法详解
Dec 07 Javascript
layui实现左侧菜单点击右侧内容区显示
Jul 26 Javascript
Vue3新特性之在Composition API中使用CSS Modules
Jul 13 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
header()函数使用说明
2006/11/23 PHP
主流PHP框架的优缺点对比分析
2014/12/25 PHP
php fseek函数读取大文件两种方法
2016/10/12 PHP
jQuery阻止同类型事件小结
2013/04/19 Javascript
jquery快捷动态绑定键盘事件的操作函数代码
2013/10/17 Javascript
jQuery中事件对象e的事件冒泡用法示例介绍
2014/04/25 Javascript
jQuery中select与datalist制作下拉菜单时的区别浅析
2016/12/30 Javascript
ajax与json 获取数据并在前台使用简单实例
2017/01/19 Javascript
Angularjs验证用户输入的字符串是否为日期时间
2017/06/01 Javascript
利用types增强vscode中js代码提示功能详解
2017/07/07 Javascript
javascript Function函数理解与实战
2017/12/01 Javascript
详解微信小程序支付流程与梳理
2019/07/16 Javascript
Vue 同步异步存值取值实现案例
2020/08/05 Javascript
简单介绍Python下自己编写web框架的一些要点
2015/04/29 Python
python使用WMI检测windows系统信息、硬盘信息、网卡信息的方法
2015/05/15 Python
python生成器,可迭代对象,迭代器区别和联系
2018/02/04 Python
Win8.1下安装Python3.6提示0x80240017错误的解决方法
2018/07/31 Python
python3爬虫获取html内容及各属性值的方法
2018/12/17 Python
python 对多个csv文件分别进行处理的方法
2019/01/07 Python
python处理DICOM并计算三维模型体积
2019/02/26 Python
python读取mysql数据绘制条形图
2020/03/25 Python
Python3安装模块报错Microsoft Visual C++ 14.0 is required的解决方法
2020/07/28 Python
用Python实现童年贪吃蛇小游戏功能的实例代码
2020/12/07 Python
Sandro法国官网:法国成衣品牌
2019/08/28 全球购物
写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数
2016/04/13 面试题
端午节粽子促销活动方案
2014/02/02 职场文书
银行简历自我评价
2014/02/11 职场文书
文明城市标语
2014/06/16 职场文书
售房委托书
2014/08/30 职场文书
单位实习工作证明怎么写
2014/11/02 职场文书
2014年安全保卫工作总结
2014/11/13 职场文书
2014年大堂经理工作总结
2014/11/21 职场文书
一年级语文上册复习计划
2015/01/17 职场文书
企业安全隐患排查治理制度
2015/08/05 职场文书
导游词之无锡古运河
2019/11/14 职场文书
golang内置函数len的小技巧
2021/07/25 Golang