探究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 ...
随机推荐
-
SAP CRM 显示消息/在消息中进行导航
向用户展示消息,在任何软件中都是十分重要的. 在SAP CRM WEB UI中展示消息,不是一项很难的任务,只需要创建消息并在之后调用方法来显示它 消息类和消息号: 我在SE91中创建了如下的消息类和 ...
-
[LeetCode] Single Number 单独的数字
Given an array of integers, every element appears twice except for one. Find that single one. Note:Y ...
-
Freemarker 入门示例(zhuan)
http://cuisuqiang.iteye.com/blog/2031768 ************************************ 初步学习freemarker ,先做一个简单 ...
-
Unity3D研究院编辑器之不实例化Prefab获取删除更新组件(十五)
http://www.xuanyusong.com/archives/3727 感谢楼下的牛逼回复更正一下,我表示我也是才知道.. 其实不需要实例化也能查找,你依然直接用GetComponentsIn ...
-
使用Visual Studio 2013 从头构建Web表单
在这篇文章中,我将采取VS 2013中特定的模板,也就是没有身份验证的Web表单模板,并说明如何构建这个项目从头开始.在本教程的最后,你会最终有一个模板,内容几乎是一样的使用Web表单模板没有认证(文 ...
-
未找到或无法访问服务器 请验证实例名称是否正确并且SQL Server 已配置为允许远程连接
无法连接到sql server 2008服务器 报下错误 其他信息 在与SQL Server建立连接时出现与网络相关的或特定于实例的错误 未找到或无法访问服务器请验证实例名称是否正确并且SQL ...
-
在 DotNetCore 3.0 程序中使用通用协议方式启动文件关联应用
问题描述 在传统的基于 .NET Framework 的 WPF 程序中,我们可以使用如下代码段启动相关的默认应用: # 启动默认文本编辑器打开 helloworld.txt Process.Star ...
-
了解Java基本数据类型的取值范围
拿byte类型做栗子 一个字节是8位二进制数,然后最高位会用来作为符号位.正数计算机是存的原码,负数是存的补码. 也就说byte正数最大是0111 1111,转化为十进制是:127(这就是byte的上 ...
-
loadrunner中pacing设置01
之前一直也用pacing值来调节TPS,一直觉得它和think time没啥区别.这次项目中,和同事就此展开了讨论,细细一研究发现pacing值门道还是很多的. 如下面三个图: 上图是pacing的三 ...
-
洛谷 P3803 多项式乘法
题目背景 这是一道FFT模板题 题目描述 给定一个n次多项式F(x),和一个m次多项式G(x). 请求出F(x)和G(x)的卷积. 输入输出格式 输入格式: 第一行2个正整数n,m. 接下来一行n+1 ...