探究JavaScript中的五种事件处理程序
探究JavaScript中的五种事件处理程序
我们知道JavaScript与HTML之间的交互是通过事件实现的。事件最早是在IE3和Netscape Navigator 2中出现的,当时是作为分担服务器运算负载的一种手段。 通俗地理解,事件就是用户或浏览器自身执行的某种操作。而事件处理程序即为响应某个事件的函数。抽出主干,即事件处理程序为函数。 我们又把事件处理程序称为事件侦听器。 事件处理程序是以"on"开头的,因此对于事件on的时间处理程序即为onclick。时间处理程序在JavaScript中大致有五种,下面会根据这五种不同的时间处理程序分为5部分来介绍。
第一部分:HTML事件处理程序
什么使HTML事件处理程序呢?显然,通过名字就可以猜到,它是卸载HTML中的函数(事件处理程序)。初学者大多用到的事件处理程序即为HTML事件处理程序。下面举例:
例1:
<button onclick="alert('success')">点我</button>
这条代码即为事件处理程序,点击button后,会弹出弹框,显示success。
特点:HTML事件处理程序中Javascript代码作为了onclick特性的值,因此,我们不能在JavaScript代码中使用未经转义的HTML语法字符,如&(和号)、""(双引号)、<(小于号)、>(大于号)等等。所以这个例子中字符串我使用了单引号而没有使用双引号。看下面在JavaScript代码中使用了未经转义的HTML语法字符。
例2:
<button onclick="alert("success")">点我</button>
这时,我在success外使用了HTML语法字符""(双引号),这时不会弹出窗口,而是报错语法错误。但是我如果还是希望使用双引号呢? 这时就要用"实体来代替HTML中的语法字符。如下例所示:
例3:
<button onclick="alert("success")">点我</button>
<!-- 正常弹出窗口-->
这个例子中我们在JavaScript代码中使用了HTML实体而没有使用HTML语法字符,这时就不会报错了。
例4:
<button onclick="show()">点我</button>
<!-- 正常弹出窗口--> <script>
function show(){
alert("success");
}
</script>
这个例子中我们调用函数,而把函数定义放在了script中,这样也是可以的。因为:事件处理程序中的代码在执行时,有权访问到全局作用域中的任何代码。这句话怎么理解呢? 实际上,我们可以在chrome中观察button标签的作用域链。如下所示:
接下来我们再看看script所在的作用域,如下图所示:
可以看到script标签就在全局作用域。
也就是说目前button中的HTML事件处理函数在作用域链的最前端,而Script在全局作用域,所以“事件处理程序中的代码在执行时,有权访问到全局作用域中的任何代码。”这句话就不难理解了。
例5:
<button onclick="alert(event.type)">点我</button>
这时浏览器弹出窗口显示:click。这个例子是什么意思呢?注意到我并没有在event.type外加单引号,说明这并不是字符串。实际上,event是局部对象--在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件有关的信息。而这里是弹出了对象了类型,即为click。
HTML事件处理程序的三个缺点(重点):
1. 时差问题。 因为用户可能在HTML元素一出现就开始触发相应事件,但是有可能该事件的脚本(如例4中show()函数的函数定义在script中)还没有加载完成,此时不具备执行条件,故报错。
解决方法:将HTML事件处理程序封装在一个try-catch块中,以便错误不会浮出水面。
<input type="button" value="click me" onclick="try{show();}catch(ex){}">
2.这样扩展事件实例程序的作用域链在不同的浏览器中会导致不同的结果(例4中我是在chrome中查看的作用域链,其他浏览器不一定是这样的,请注意)。不同JavaScript引擎遵循的标识符解析规则略有差异,很有可能会在访问非限定对象成员时出错。
3.HTML和JavaScript代码紧密耦合。 结果是:如果要更换事件处理程序,就必须改动两个地方--HTML代码和JavaScript代码。
那么怎么解决上面的问题呢? DOM0级事件处理程序是一个不错的选择!
第二部分:DOM0级事件处理程序
DOM0级事件处理程序用的也非常普遍。之所以成为DOM0级,我认为是当时还没有出DOM标准,而IE和Netscape Navigator两者使用的时间处理程序(不知是否合理,望批评指正)。 总之,我们先看看下面的例子吧。
例6:
<button id="button">点我</button> <script>
var button=document.getElementById("button");
button.onclick=function(){
alert("clicked");
}
</script>
即我们先在script中取得元素的引用,然后再将一个函数赋值给onclick事件处理程序。 之前介绍过,事件处理程序即为函数,而button.onclick这种形式即函数作为了对象的方法。那么对象的方法即事件处理程序是在元素(对象)的作用域中运行而非在全局作用域中运行的,因为方法是属于对象的。(注意:例4中事件处理程序是在全局作用域中运行的)。 如果这个函数中存在this关键字,那么this就会指向这个对象。下面我们在浏览器中证明事件处理程序是在元素的作用域中运行。
我们看到alert("clicked");确实是在button中运行的。
我们还可以通过下面的方式删除通过DOM0级方法指定的事件处理程序。
button.onclick=null;
通过上面的分析我们可以知道DOM0级事件处理程序是非常不错的,它解决了HTML事件处理程序的三个缺点:时差问题、作用域链导致的不同浏览器表现不一致问题和HTML和JavaScript紧密耦合问题。
但是,DOM0级事件处理程序并不是完美的,它同样有两个缺点:
- 我们不能给一个元素同时添加两个事件。
- 我们不能控制元素的事件流(捕获or冒泡)。
对于第二个问题后面会讲到,第一个问题举例如下:
<button id="button">点我</button> <script>
var button=document.getElementById("button");
button.onclick=function(){
alert("clicked");
}
button.onclick=function(){
alert("again");
}
虽然我对同一个元素设置了两个事件处理程序,但是最终的结果是:只有第二个事件有效(覆盖了第一个事件)。当然,人类是聪明的动物,DOM2级事件很好的解决了这个问题!
第三部分:DOM2级事件处理程序
DOM2级事件处理程序定义了两个方法:
- addEventListener() ---添加事件侦听器
- removeEventListener() ---删除事件侦听器
在博文的开头我就提到了事件处理程序即事件侦听器。这两个方法都接收三个参数:
- 要处理的事件名(注意:是时间名,所以没有on!),如click、mouseover等。
- 作为事件处理程序的函数,如function(){alert("clicked");}
- 表示事件流方式的布尔值。false为冒泡阶段调用事件处理程序;true为捕获阶段调用事件处理程序。对于冒泡和捕获这两种时间流可以看《JavaScript中的两种事件流》
下面通过两个例子加深理解:
例7:
<button id="button">点我</button> <script>
var button=document.getElementById("button");
button.addEventListener("click",function(){
alert(this.id);
},false);
button.addEventListener("click",function(){
alert("another event");
},false);
</script>
结果:第一次弹出窗口:button。
第二次弹出窗口:another event。
结论:通过DOM2级事件处理程序,我们可以为同一个元素添加两个或更多的事件。事件根据顺序依次触发。且this同样指向当前元素,故函数在元素的作用域中执行。
this分析:和前面的DOM0级事件处理程序一样,这里的addEventListener同样也可以看作对象的方法,不同之初在于,DOM0级的方法需要另外一个函数来赋值,而这里的方法是DOM2级规范预定义的。
removeEventListener()这个删除事件处理程序的方法值得注意的是:使用addEventListener()来添加的事件处理程序只能通过它来移除,且需要传入相同的参数。
例8:
<button id="button">点我</button> <script>
var button=document.getElementById("button");
button.addEventListener("click",function(){
alert(this.id);
},false);
button.removeEventListener("click",function(){
alert("another event");
},false);
上述代码貌似可以移除click的事件处理程序,但是通过实验证明是不可以的,原因是:事件处理程序为匿名函数时无法移除。看下面的成功移除的例子:
例9:
<button id="button">点我</button> <script>
var button=document.getElementById("button");
function handler(){
alert(this.id);
}
button.addEventListener("click",handler,false);
button.removeEventListener("click",handler,false);
</script>
成功移除!
注意:1.传入方法的handler没有(),是因为这里都只是定义函数,而不是调用,需要注意。
2.这两个方法的第三个参数都是false,即事件处理程序添加到冒泡阶段。一般不使用true,因为低版本的IE不支持捕获阶段。
DOM2级事件处理程序成功地解决了前面所有事件处理程序的问题,堪称perfect!!!! 然而总是特立独行的IE浏览器又有新花样,它也有自己的一套事件处理程序,下面我们就来看看吧。
第四部分:IE事件处理程序
IE事件处理程序中有类似与DOM2级事件处理程序的两个方法:
- attachEvent()
- detachEvent()
它们都接收两个参数:
- 事件处理程序名称。如onclick、onmouseover,注意:这里不是事件,而是事件处理程序的名称,所以有on。
- 事件处理程序函数。如function(){alert("clicked");}
之所以没有和DOM2级事件处理程序中类似的第三个参数,是因为IE8及更早版本只支持冒泡事件流。
注意:
1.IE事件处理程序中attachEvent()的事件处理程序的作用域和DOM0与DOM2不同,她的作用域是在全局作用域中。因此,不同于DOM0和DOM2中this指向元素,IE中的this指向window。
2.同样,我们可以使用attachEvent()来给同一个元素添加多个事件处理程序。但是与DOM2不同,事件触发的顺序不是添加的顺序而是添加顺序的相反顺序。
3.同样地,通过attachEvent()添加的事件处理程序必须通过detachEvent()方法移除,同样的,不能使用匿名函数。
4.支持IE事件处理程序的浏览器不只有IE浏览器,还有Opera浏览器。
第五部分:跨浏览器的事件处理程序
实际上,这一部分视为了跨浏览器使用,将前面的几部分结合起来就可以了。
这一部分需要创建两个方法:
- addHandler() --这个方法职责是视情况来使用DOM0级、DOM2级、IE事件处理程序来添加事件。
- removeHandler()--这个方法就是移除使用addHandler添加的事件。
这两个方法接收相同的三个参数:
- 要操作的元素--通过dom方法获取
- 事件名称--注意:没有on,如click、mouseover
- 事件处理程序函数--即handler函数
这两个方法的构造情况如下:
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type]=handler;
}
},
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type]=null;
}
}
};
即先判断DOM2级事件处理程序,再判断IE事件处理程序,最后使用DOM0级事件处理程序。
例10:通过这个例子来使用上面构造的方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>跨浏览器事件处理程序</title>
</head>
<body> <button id="button">点我</button> <script>
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type]=handler;
}
},
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type]=null;
}
}
}; function handler(){
alert("clicked");
} var button=document.getElementById("button");
EventUtil.addHandler(button,"click",handler);
</script>
</body>
</html>
最后浏览器成功弹出“clicked”。
探究JavaScript中的五种事件处理程序的更多相关文章
- Javascript中的五种数据类型
Undefined 未定义.只有一个值undefined Null 只有一个值,null Boolean 在javascript中,只要逻辑表达式不返回undefined不返回null ...
- Linux 中的五种 IO 模型
Linux 中的五种 IO 模型 在正式开始讲Linux IO模型前,比如:同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一 ...
- 在Javascript中监听flash事件(转)
在Javascript中监听flash事件,其实有两种做法: 1.在特定的环境下(例如专门制作的flash),大家约定一个全局函数,然后在flash的事件中用ExternalInterface.cal ...
- Odoo中的五种Action详解
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826232.html Odoo中的五种action都是继承自ir.actions.actions模型实现的 ...
- Oracle--数据库中的五种约束
数据库中的五种约束 数据库中的五种约束及其添加方法 五大约束 1.--主键约束(Primay Key Coustraint) 唯一性,非空性 2.--唯一约束 (Unique Counstraint ...
- JavaScript中的鼠标滚轮事件详解
JavaScript中的鼠标滚轮事件详解/*Firefox注册事件*/ ~~~Firefox: addEventListener('DOMMouseScroll', handler, false)if ...
- Java中的五种单例模式实现方法
[代码] Java中的五种单例模式实现方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 2 ...
- JavaScript中的三种弹出对话框
学习过js的小伙伴会发现,我们在一些实例中用到了alert()方法.prompt()方法.prompt()方法,他们都是在屏幕上弹出一个对话框,并且在上面显示括号内的内容,使用这种方法使得页面的交互性 ...
- JavaScript中的三种弹出框的区别与使用
JavaScript中有三种原生的弹出框,分别是alert.confirm.prompt.分别表示弹出框.确认框.信息框. 以下是示例代码: <!DOCTYPE html> <htm ...
随机推荐
- js基础知识温习:Javascript中如何模拟私有方法
本文涉及的主题虽然很基础,在很多人眼里属于小伎俩,但在JavaScript基础知识中属于一个综合性的话题.这里会涉及到对象属性的封装.原型.构造函数.闭包以及立即执行表达式等知识. 公有方法 公有方法 ...
- Loom工具类:Unity3D巧妙处理多线程
Loom代码不多,只有168行, 然而却具备了子线程运行Action, 子线程与主线程交互的能力! public static Thread RunAsync(Action a) public sta ...
- JavaScript学习笔记-循环输出菱形,并可菱形自定义大小
var Cen = 6;//定义菱形中部为第几行(起始值为0) //for循环输出菱形 document.write("<button onclick='xh()'>点我for循 ...
- 来个linq to js
说这个话题之前,我们来讲一下C#的linq 语法.在C#里面我们会对列表进行操作,如OrderBy(p=>p.property),Where(p=>p.property==..) 括号里 ...
- eclipse的历史版本及下载
有时候我们总会遇到在需要eclipse而无法及时找到的时候, 所以那些有用的链接, 是帮助我们能够及时找到我们想要版本的额最好方式 Eclipse 3.1 IO[木卫一,伊奥] 2005 http:/ ...
- 模块(modue)的概念:
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很 ...
- Java发送邮件代码
MailSenderInfo.java package com.nihaorz.mail.util; import java.util.Properties; public class MailSen ...
- 【POJ 2942】Knights of the Round Table(点双连通分量,二分图染色)
圆桌会议必须满足:奇数个人参与,相邻的不能是敌人(敌人关系是无向边). 求无论如何都不能参加会议的骑士个数.只需求哪些骑士是可以参加的. 我们求原图的补图:只要不是敌人的两个人就连边. 在补图的一个奇 ...
- AFnetworking3.1的基本使用
听说之后AFHttpWorking版本可能会影响到苹果的审核,今天下了最新版本的AFHttpWorking,并且做了简单的封装,我这里是通过cocoapods下载了两个工具 1=AFHttpWorki ...
- angularjs 自带的过滤器
一,内置的过滤器 1,uppercase,lowercase大小转换 {{ "lower cap string" | uppercase }} //结果:LOWER CAP ...