一、开篇

在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章。犹记得去年这个时候,参加百度的实习生面试,被问到事件模型,当时被问的一头雾水,平时敲onclick敲的挺爽,却没有关注到事件模型的整体概念。这个周末难得清闲,决定就javascript中的事件模型写个系列,算是对知识点的一个总结,也是对自己的一个交代。

初步计划分为以下几个部分:

    ①   javascript事件的基本概念及基于原始、IE、DOM2的三种模型的异同点

    ②   javascript事件流介绍,捕获-冒泡机制及事件委托机制

    ③   jquery中的事件监听方式(bind、live、attachEvent、on)及异同点

    ④   javascript自定义事件

二、event简介

什么是事件呢?直观的说就是网页上发生的事情,大部分是指用户的鼠标动作和键盘动作,如点击、移动鼠标、按下某个键。为什么说大部分呢,因为事件不单单只有这两部分,还有其他的例如document的load和unloaded。只不过我们更加关注的是用户的操作。事件被封装成一个event对象,包含了该事件发生时的所有相关信息(event的属性)以及可以对事件进行的操作(event的方法)。

event长啥样呢,来直观的看一下,比如我点击页面上一个按钮,产生的event对象如下:

可以看到是一个MouseEvent对象,包含了一系列属性,如鼠标点击的位置等。那么敲击键盘时产生的event对象和它一样吗?看看就知道:

    可以看到是一个KeyboardEvent对象,并且属性跟上面的也不太一样,如没有clientX/Y,那是理所当然的啦,敲键盘怎么能获取到鼠标的位置呢。

若你有一点面向对象编程的基础,看到这两个类名应该会有所思考,MouseEvent、KeyboardEvent会不会是继承自一个叫Event的类呢?恭喜你猜对了,确实如此。来看一下,我在window.onload监听函数中打印出event对象如下:

    属性少了很多,毕竟是父类嘛。若你想了解更多关于事件类型的内容,可以参考这里,本文就不做更深的介绍。

三、event对象常用属性、方法

1. 事件定位相关属性

    这部分属性平时用的还是挺多的,所以得着重介绍。如果你细细看了MouseEvent对象里的属性,一定发现了有很多带X/Y的属性,它们都和事件的位置相关。具体包括:x/y、clientX/clientY、pageX/pageY、screenX/screenY、layerX/layerY、offsetX/offset 六对。有点乱了吧,一个点击事件能有多少位置啊?不要着急,其实并不复杂,之所以能有这么多是因为各浏览器厂商在版本更迭的时候产生了很多不一致。看下面的例子就明白各自的含义了:

在这里移动鼠标
x:   y:
clientX:  
clientY:
screenX:  
screenY:
offsetX:  
offsetY:
pageX:  
pageY:
layerX:  
layerY:

    得出的结论如下:

    x/y与clientX/clientY值一样,表示距浏览器可视区域(工具栏除外区域)左/上的距离;

    pageX/pageY,距页面左/上的距离,它与clientX/clientY的区别是不随滚动条的位置变化;

    screenX/screenY,距计算机显示器左/上的距离,拖动你的浏览器窗口位置可以看到变化;

    layerX/layerY与offsetX/offsetY值一样,表示距有定位属性的父元素左/上的距离。

    之所以有那么多值一样的情况,就是由于浏览器兼容的原因。那我们平时该如何使用呢?请看下面的表格,列出了各属性的浏览器支持情况。(+支持,-不支持)

    offsetX/offsetY:W3C- IE+ Firefox- Opera+ Safari+ chrome+

    x/y:W3C- IE+ Firefox- Opera+ Safari+ chrome+

    layerX/layerY:W3C- IE- Firefox+ Opera- Safari+ chrome+

    pageX/pageY:W3C- IE- Firefox+ Opera+ Safari+ chrome+

    clientX/clientY:W3C+ IE+ Firefox+ Opera+ Safari+ chrome+

    screenX/screenY:W3C+ IE+ Firefox+ Opera+ Safari+ chrome+​

  说明:该表摘自其他文章,我未做全部验证,但从最新版本的现代浏览器来看,这些属性貌似是都支持了,为了更好的兼容性,通常我们选择W3C支持的就可以了。若你想看更加细致的相关描述,请点击这里

2.其他常用属性

    target:发生事件的节点;

    currentTarget:当前正在处理的事件的节点,在事件捕获或冒泡阶段;

      timeStamp:事件发生的时间,时间戳。

    bubbles:事件是否冒泡。

    cancelable:事件是否可以用preventDefault()方法来取消默认的动作;

    keyCode:按下的键的值;

3. event对象的方法

    event. preventDefault()//阻止元素默认的行为,如链接的跳转、表单的提交;

    event. stopPropagation()//阻止事件冒泡

    event.initEvent()//初始化新事件对象的属性,自定义事件会用,不常用

    event. stopImmediatePropagation()//可以阻止掉同一事件的其他优先级较低的侦听器的处理,(我没有用过)

四、事件的三种模型

由于复杂的历史原因,事件模型是不统一的,当然作为前端开发人员这种事情已经见怪不怪了。尽管W3C已经制定了DOM2标准来规范事件的定义,但由于顽固的IE6、7、8存在,我们还是得清楚IE的那一套定义。那么来看看三种模型都有哪些吧。

1.     原始事件模型

    在原始事件模型中(也有说DOM0级),事件发生后没有传播的概念,没有事件流。事件发生,马上处理,完事,就这么简单。监听函数只是元素的一个属性值,通过指定元素的属性值来绑定监听器。书写方式有两种:

    ①   HTML代码中指定属性值:<input type=”button” onclick=”func1()” />

    ②   在js代码中指定属性值:document.getElementsByTagName(‘input’)[0].onclick = func1

    优点:所有浏览器都兼容

    缺点:1)逻辑与显示没有分离;2)相同事件的监听函数只能绑定一个,后绑定的会覆盖掉前面的,如:a.onclick = func1; a.onclick = func2;将只会执行func2中的内容。3)无法通过事件的冒泡、委托等机制(后面系列会讲到)完成更多事情。

    在当前web程序模块化开发以及更加复杂的逻辑状况下,这种方式显然已经落伍了,所以在真正项目中不推荐使用,平时写点博客小例子啥的倒是可以,速度比较快。

2.     IE事件模型

    在参考其他资料时,我有看到这样的一句话“IE不把该对象传入事件处理函数,由于在任意时刻只会存在一个事件,所以IE把它作为全局对象window的一个属性”,为求证其真伪,我用IE8执行了代码alert(window.event),结果弹出是null,说明该属性已经定义,只是值为null(与undefined不同)。我想难道这个全局对象的属性是在监听函数里才加的?于是执行下面代码:

    window.onload = function (){alert(window.event);}

    setTimeout(function(){alert(window.event);},2000);

    结果第一次弹出【object event】,两秒后弹出依然是null。由此可见IE是将event对象在处理函数中设为window的属性,一旦函数执行结束,便被置为null了。IE的事件模型只有两步,先执行元素的监听函数,然后事件沿着父节点一直冒泡到document。冒泡机制后面系列会讲,此处暂记。IE模型下的事件监听方式也挺独特,绑定监听函数的方法是:attachEvent( "eventType","handler"),其中evetType为事件的类型,如onclick,注意要加’on’。解除事件监听器的方法是 detachEvent("eventType","handler" )

    IE的事件模型已经可以解决原始模型的三个缺点,但其自己的缺点就是兼容性,只有IE系列浏览器才可以这样写。

3.     DOM2事件模型

    此模型是W3C制定的标准模型,既然是标准,那大家都得按这个来,我们现在使用的现代浏览器(指IE6~8除外的浏览器)都已经遵循这个规范。W3C制定的事件模型中,一次事件的发生包含三个过程:

    (1)capturing phase:事件捕获阶段。事件被从document一直向下传播到目标元素,在这过程中依次检查经过的节点是否注册了该事件的监听函数,若有则执行。

    (2)target phase:事件处理阶段。事件到达目标元素,执行目标元素的事件处理函数.

    (3)bubbling phase:事件冒泡阶段。事件从目标元素上升一直到达document,同样依次检查经过的节点是否注册了该事件的监听函数,有则执行。

    所有的事件类型都会经历captruing phase但是只有部分事件会经历bubbling phase阶段,例如submit事件就不会被冒泡。

    你可能会有疑问,为什么是这个样子的呢?流程有点太多了吧?事情的缘由还得从网景公司与微软争霸开始说起。在W3C的规范还没有出生的时候,市场上已经有两家强劲的浏览器厂商,产品分别是微软的IE和网景的Netspace Navigator(后面简称NN),IE的事件模型上面已介绍,事件是可以冒泡的。然而NN却不这么认为,它的模型中,事件是从上往下走的,即只有捕获阶段。两家都没有谁对谁错,因为按照他们的模型都可以完成事件的处理。然后W3C珊珊来迟,要制定标准,要统一,所以也就只能两家的都采纳,谁也不得罪,然后用标准制定者的口吻宣布:W3C模型工作良好。从此天下太平。

    说远了,赶紧来看看标准的事件监听器该如何绑定:addEventListener("eventType","handler","true|false");其中eventType指事件类型,注意不要加‘on’前缀,与IE下不同。第二个参数是处理函数,第三个即用来指定是否在捕获阶段进行处理,一般设为false来与IE保持一致,除非你有特殊的逻辑需求。监听器的解除也类似:removeEventListner("eventType","handler","true!false");

        以上便是事件的三种模型,我们在开发的时候需要兼顾IE与非IE浏览器,所以注册一个监听器应该这样写:

  1. var a = document.getElementById('a');
  2. if(a.attachEvent){
  3. a.attachEvent('onclick',func);
  4. }
  5. else{
  6. a.addEventListener('click',func,false);
  7. }

  感觉很麻烦吧?因此我们一般会借助现有框架或类库已经封装好的,比如jQuery,后面将会介绍jQuery中强大的事件监听方式。

    系列一到此结束,作者本人技术水平有限,文章内容都是自己的理解写出来的,欢迎各路高手指点纠错。

Javascript事件模型系列(一)事件及事件的三种模型的更多相关文章

  1. SDN三种模型解析

    数十年前,计算机科学家兼网络作家Andrew S. Tanenbaum讽刺标准过多难以选择,当然现在也是如此,比如软件定义网络模型的数量也很多.但是在考虑部署软件定义网络(SDN)或者试点之前,首先需 ...

  2. JavaScript:学习笔记(7)——VAR、LET、CONST三种变量声明的区别

    JavaScript:学习笔记(7)——VAR.LET.CONST三种变量声明的区别 ES2015(ES6)带来了许多闪亮的新功能,自2017年以来,许多JavaScript开发人员已经熟悉并开始使用 ...

  3. PHP.23-ThinkPHP框架的三种模型实例化-(D()方法与M()方法的区别)

    三种模型实例化 原则上:每个数据表应对应一个模型类(Home/Model/GoodsModel.class.php --> 表tp_goods) 1.直接实例化 和实例化其他类库一样实例化模型类 ...

  4. C语言提高 (3) 第三天 二级指针的三种模型 栈上指针数组、栈上二维数组、堆上开辟空间

    1 作业讲解 指针间接操作的三个必要条件 两个变量 其中一个是指针 建立关联:用一个指针指向另一个地址 * 简述sizeof和strlen的区别 strlen求字符串长度,字符数组到’\0’就结束 s ...

  5. 深入理解DOM事件机制系列第四篇——事件模拟

    × 目录 [1]引入 [2]模拟机制 [3]自定义事件 前面的话 事件是网页中某个特别的瞬间,经常由用户操作或通过其他浏览器功能来触发.但实际上,也可以使用javascript在任意时刻来触发特定的事 ...

  6. 深入理解DOM事件类型系列第一篇——鼠标事件

    × 目录 [1]类型 [2]顺序 [3]坐标位置[4]修改键[5]相关元素[6]鼠标按键[7]滚轮事件[8]移动设备 前面的话 鼠标事件是web开发中最常用的一类事件,毕竟鼠标是最主要的定位设备.本文 ...

  7. JS003. 事件监听和监听滚动条的三种参数( addEventListener( ) )

    全局 1 window.addEventListener('scroll', () => { 2 console.log('------') 3 console.log(document.doc ...

  8. 【SSH系列】-- Hibernate持久化对象的三种状态

    在上一篇博文中,小编主要简单的介绍了[SSH系列]--hibernate基本原理&&入门demo,今天小编来继续介绍hibernate的相关知识, 大家知道,Java对象的生命周期,是 ...

  9. Storm 系列(六)—— Storm 项目三种打包方式对比分析

    一.简介 在将 Storm Topology 提交到服务器集群运行时,需要先将项目进行打包.本文主要对比分析各种打包方式,并将打包过程中需要注意的事项进行说明.主要打包方式有以下三种: 第一种:不加任 ...

随机推荐

  1. python学习之路-day12-mysql && orm

    一.数据库 1.数据库介绍 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据.我们也可以将数据存储 ...

  2. BLE资料应用笔记 -- 持续更新

    BLE资料应用笔记 -- 持续更新 BLE 应用笔记 小书匠 简而言之,蓝牙无处不在,易于使用,低耗能和低使用成本.'让我们'更深入地探索这些方面吧. 蓝牙无处不在-,您可以在几乎每一台电话.笔记本电 ...

  3. spring+struts2的jar

  4. javascript里的封装

    用javascript闭包的特性,可以模拟实现私有变量.私有方法. var myObject = =(function(){ var privateValue; function privateMet ...

  5. 【Java】使用iText生成PDF文件

    iText介绍 iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库.通过iText不仅可以生成PDF或rtf的文档,而且可以将XML.Html文件转 ...

  6. ZOJ3774_Power of Fibonacci

    求fibonacci数列前N个数的K次方和. 通项公式:F[n]=((1+sqrt(5))/sqrt(5)-(1-sqrt(5))/sqrt(5))/sqrt(5). 有点乱,不过由于可以保证最后的结 ...

  7. java中数组的相关知识

      1. 2.数组的命名方法 1)int[]ages=new int[5]; 2) int[]ages; ages=new int[5]; 3)int[]ags={1,2,3,4,5}; 4)int[ ...

  8. ubuntu 14.04 下evolution邮箱客户端设置(腾讯企业邮箱)

    安装 evolution 有PPA可用,支持 Ubuntu 14.04 及衍生系统.打开终端,输入以下命令: sudo add-apt-repository ppa:fta/gnome3 sudo a ...

  9. [LeetCode] 452 Minimum Number of Arrows to Burst Balloons

    There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided ...

  10. android TCP 客户端(仅接收数据)

    配合log4net使用,用来接收调试信息.因此,此客户端只管通过TCP接收字符串数据,然后显示在界面上. 接收TCP数据 try { Socket s = new Socket("192.1 ...