Javascript事件模型系列(一)事件及事件的三种模型

1、开篇

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

         初步计划分为如下几个部分:前端

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

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

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

    ④   javascript自定义事件面试

2、event简介

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

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

 

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

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

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

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

3、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()//能够阻止掉同一事件的其余优先级较低的侦听器的处理,(我没有用过)

4、事件的三种模型

           因为复杂的历史缘由,事件模型是不统一的,固然做为前端开发人员这种事情已经见怪不怪了。尽管W3C已经制定了DOM2标准来规范事件的定义,但因为顽固的IE六、七、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浏览器,因此注册一个监听器应该这样写:

var a = document.getElementById('a');
if(a.attachEvent){
    a.attachEvent('onclick',func);
}
else{
    a.addEventListener('click',func,false);
}

        感受很麻烦吧?所以咱们通常会借助现有框架或类库已经封装好的,好比jQuery,后面将会介绍jQuery中强大的事件监听方式。

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

相关文章
相关标签/搜索