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 图片缩放效果代码
Jun 09 Javascript
Jquery遍历checkbox获取选中项value值的方法
Feb 13 Javascript
跟我学习javascript的闭包
Nov 16 Javascript
前端学习笔记style,currentStyle,getComputedStyle的用法与区别
May 28 Javascript
JQuery 设置checkbox值二次无效的解决方法
Jul 22 Javascript
js简单正则验证汉字英文及下划线的方法
Nov 28 Javascript
微信小程序 实战实例开发流程详细介绍
Jan 05 Javascript
微信小程序 图片宽度自适应的实现
Apr 06 Javascript
JS 60秒后重新发送验证码的实例讲解
Jul 26 Javascript
详解AngularJS1.x学习directive 中‘&amp; ’‘=’ ‘@’符号的区别使用
Aug 23 Javascript
JavaScript实现栈结构Stack过程详解
Mar 07 Javascript
基于JS实现操作成功之后自动跳转页面
Sep 25 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
德生PL550的电路分析
2021/03/02 无线电
解析php框架codeigniter中如何使用框架的session
2013/06/24 PHP
php 邮件发送问题解决
2014/03/22 PHP
ThinkPHP中Widget扩展的两种写法及调用方法详解
2017/05/04 PHP
解决Laravel5.2 Auth认证退出失效的问题
2019/10/14 PHP
javascript 火狐(firefox)不显示本地图片问题解决
2008/07/05 Javascript
JQuery index()方法使用代码
2010/06/02 Javascript
js中判断用户输入的值是否为空的简单实例
2013/12/23 Javascript
javascript实现当前页导航激活的方法
2015/02/27 Javascript
Node.js模块封装及使用方法
2016/03/06 Javascript
jquery UI Datepicker时间控件的使用及问题解决
2016/04/28 Javascript
全面解析DOM操作和jQuery实现选项移动操作代码分享
2016/06/07 Javascript
Spring shiro + bootstrap + jquery.validate 实现登录、注册功能
2017/06/02 jQuery
在Vue methods中调用filters里的过滤器实例
2018/08/30 Javascript
详解CommonJS和ES6模块循环加载处理的区别
2018/12/26 Javascript
微信小程序实现单选选项卡切换效果
2020/06/19 Javascript
[03:06]2018年度CS GO最具人气解说-完美盛典
2018/12/16 DOTA
Python3字符串学习教程
2015/08/20 Python
Python SVM(支持向量机)实现方法完整示例
2018/06/19 Python
python tkinter图形界面代码统计工具
2019/09/18 Python
python 截取XML中bndbox的坐标中的图像,另存为jpg的实例
2020/03/10 Python
python属于跨平台语言码
2020/06/09 Python
全面解析CSS Media媒体查询使用操作(推荐)
2017/08/15 HTML / CSS
css3实现3D色子翻转特效
2014/12/23 HTML / CSS
css3类选择器之结合元素选择器和多类选择器用法
2017/03/09 HTML / CSS
HTML5 canvas画矩形时出现边框样式不一致的解决方法
2013/10/14 HTML / CSS
澳大利亚最早和最古老的巨型游戏专家:Yardgames
2020/02/20 全球购物
上海期货面试题
2014/01/31 面试题
内科护士实习自我鉴定
2013/10/17 职场文书
学校介绍信范文
2014/01/14 职场文书
银行青年文明号事迹材料
2014/05/31 职场文书
学生通报表扬范文
2015/05/04 职场文书
思想工作总结范文
2015/08/12 职场文书
PHP实现考试倒计时功能代码
2021/04/16 PHP
python通配符之glob模块的使用详解
2021/04/24 Python
DE1103使用报告
2022/04/05 无线电