探究JavaScript中的五种事件处理程序方式


Posted in Javascript onDecember 07, 2016

我们知道JavaScript与HTML之间的交互是通过事件实现的。事件最早是在IE3和Netscape Navigator 2中出现的,当时是作为分担服务器运算负载的一种手段。  通俗地理解,事件就是用户或浏览器自身执行的某种操作。而事件处理程序即为响应某个事件的函数。抽出主干,即事件处理程序为函数。  我们又把事件处理程序称为事件侦听器。  事件处理程序是以"on"开头的,因此对于事件on的时间处理程序即为onclick。时间处理程序在JavaScript中大致有五种,下面会根据这五种不同的时间处理程序分为5部分来介绍。

1.HTML事件处理程序

2.DOM0级事件处理程序

3.DOM2级事件处理程序

4.IE事件处理程序

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标签的作用域链。如下所示:

探究JavaScript中的五种事件处理程序方式

接下来我们再看看script所在的作用域,如下图所示:

探究JavaScript中的五种事件处理程序方式

可以看到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就会指向这个对象。下面我们在浏览器中证明事件处理程序是在元素的作用域中运行。

探究JavaScript中的五种事件处理程序方式

我们看到alert("clicked");确实是在button中运行的。

我们还可以通过下面的方式删除通过DOM0级方法指定的事件处理程序。

button.onclick=null;

通过上面的分析我们可以知道DOM0级事件处理程序是非常不错的,它解决了HTML事件处理程序的三个缺点:时差问题、作用域链导致的不同浏览器表现不一致问题和HTML和JavaScript紧密耦合问题。

但是,DOM0级事件处理程序并不是完美的,它同样有两个缺点:

1.我们不能给一个元素同时添加两个事件。

2.我们不能控制元素的事件流(捕获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()   ---删除事件侦听器

在博文的开头我就提到了事件处理程序即事件侦听器。这两个方法都接收三个参数:

1.要处理的事件名(注意:是时间名,所以没有on!),如click、mouseover等。

2.作为事件处理程序的函数,如function(){alert("clicked");}

3. 表示事件流方式的布尔值。false为冒泡阶段调用事件处理程序;true为捕获阶段调用事件处理程序。

下面通过两个例子加深理解:

例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级事件处理程序的两个方法:

1.attachEvent()

2.detachEvent()

它们都接收两个参数:

1.事件处理程序名称。如onclick、onmouseover,注意:这里不是事件,而是事件处理程序的名称,所以有on。

2.事件处理程序函数。如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添加的事件。

这两个方法接收相同的三个参数:

1.要操作的元素--通过dom方法获取

2.事件名称--注意:没有on,如click、mouseover
3.事件处理程序函数--即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 相关文章推荐
jQuery的实现原理的模拟代码 -1 核心部分
Aug 01 Javascript
jquery实现的让超出显示范围外的导航自动固定屏幕最顶上
Sep 22 Javascript
jQuery 关于伪类选择符的使用说明
Apr 24 Javascript
网页广告中JS代码的信息监听示例
Apr 02 Javascript
纯js代码实现未知宽高的元素在指定元素中垂直水平居中显示
Sep 12 Javascript
js判断数组key是否存在(不用循环)的简单实例
Aug 03 Javascript
easyui取消表单实时验证,提交时统一验证的简单实例
Nov 07 Javascript
解析Vue 2.5的Diff算法
Nov 28 Javascript
Node.js创建Web、TCP服务器
Dec 05 Javascript
详解React开发必不可少的eslint配置
Feb 05 Javascript
axios使用拦截器统一处理所有的http请求的方法
Nov 02 Javascript
解决layui使用layui-icon出现默认图标的问题
Sep 11 Javascript
jquery 删除节点 添加节点 找兄弟节点的简单实现
Dec 07 #Javascript
jquery插入兄弟节点的操作方法
Dec 07 #Javascript
Bootstrap基本样式学习笔记之表格(2)
Dec 07 #Javascript
jquery的父、子、兄弟节点查找,节点的子节点循环方法
Dec 07 #Javascript
Bootstrap学习笔记之环境配置(1)
Dec 07 #Javascript
Bootstrap风格的WPF样式
Dec 07 #Javascript
Bootstrap基本组件学习笔记之导航(10)
Dec 07 #Javascript
You might like
php循环创建目录示例分享(php创建多级目录)
2014/03/04 PHP
Thinkphp实现短信验证注册功能
2016/10/18 PHP
根据key删除数组中指定的元素实现方法
2017/03/02 PHP
php+croppic.js实现剪切上传图片功能
2018/08/14 PHP
PHP xpath提取网页数据内容代码解析
2020/07/16 PHP
javascript实现的listview效果
2007/04/28 Javascript
javascript 文章截取部分无损html显示实现代码
2010/05/04 Javascript
nodejs 后缀名判断限制代码
2011/03/31 NodeJs
javascript客户端遍历控件与获取父容器对象示例代码
2014/01/06 Javascript
JS实现文字向下滚动完整实例
2015/02/06 Javascript
jQuery UI插件自定义confirm确认框的方法
2015/03/20 Javascript
jQuery实现滑动页面固定顶部显示(可根据显示位置消失与替换)
2015/10/28 Javascript
JavaScript几种数组去掉重复值的方法推荐
2016/04/12 Javascript
用JS实现图片轮播效果代码(一)
2016/06/26 Javascript
Javascript实现时间倒计时效果
2017/07/15 Javascript
Node.js使用Express.Router的方法
2017/11/14 Javascript
React styled-components设置组件属性的方法
2018/08/07 Javascript
你知道JavaScript Symbol类型怎么用吗
2020/01/08 Javascript
vue-simple-uploader上传成功之后的response获取代码
2020/09/07 Javascript
[06:04]DOTA2英雄梦之声Vol19卓尔游侠
2014/06/20 DOTA
探寻python多线程ctrl+c退出问题解决方案
2014/10/23 Python
python中Matplotlib实现绘制3D图的示例代码
2017/09/04 Python
pandas值替换方法
2018/07/10 Python
Python 中的 global 标识对变量作用域的影响
2019/08/12 Python
Python openpyxl读取单元格字体颜色过程解析
2019/09/03 Python
解决pycharm 安装numpy失败的问题
2019/12/05 Python
python数据库编程 Mysql实现通讯录
2020/03/27 Python
环境科学专业个人求职信
2013/09/26 职场文书
业务部经理岗位职责
2014/01/04 职场文书
上海世博会口号
2014/06/19 职场文书
统计学教授推荐信
2014/09/18 职场文书
民事撤诉申请书范本
2015/05/18 职场文书
刑事附带民事诉讼答辩状
2015/05/22 职场文书
无房证明样本
2015/06/17 职场文书
汤姆叔叔的小屋读书笔记
2015/06/30 职场文书
《圆的周长》教学反思
2016/02/17 职场文书