事件模型

  JavaScript事件使得网页具备互动和交互性,我们应该对其深入了解以便开发工作,在各式各样的浏览器中,JavaScript事件模型主要分为3种:原始事件模型、DOM2事件模型、IE事件模型。

  1.原始事件模型(DOM0级)

    这是一种被所有浏览器都支持的事件模型,对于原始事件而言,没有事件流,事件一旦发生将马上进行处理,有两种方式可以实现原始事件:

      (1)在html代码中直接指定属性值:<button id="demo" type="button" οnclick="doSomeTing()" />  

      (2)在js代码中为 document.getElementsById("demo").onclick = doSomeTing()

    优点:所有浏览器都兼容

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

    因为这些缺点,虽然原始事件类型兼容所有浏览器,但仍不推荐使用。

  2.DOM2事件模型

    此模型是W3C制定的标准模型,现代浏览器(IE6~8除外)都已经遵循这个规范。W3C制定的事件模型中,一次事件的发生包含三个过程:

    (1).事件捕获阶段,(2).事件目标阶段,(3).事件冒泡阶段。如下图所示

 

  事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。

  事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。

  事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。

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

  事件的传播是可以阻止的:
  • 在W3c中,使用stopPropagation()方法
  • 在IE下设置cancelBubble = true;
  在捕获的过程中stopPropagation();后,后面的冒泡过程就不会发生了。

  标准的事件监听器该如何绑定:

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

  3.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种事件模型,在我们写代码的时候,为了兼容ie,通常使用以下写法:

    var demo = document.getElementById('demo');

    if(demo.attachEvent){

     demo.attachEvent('onclick',func);

    }else{

     demo.addEventListener('click',func,false);

    }

event详解

  上面已经讲解了3种事件模型,事件,大部分情况下指的是用户的鼠标动作和键盘动作,如点击、移动鼠标、按下某个键,为什么说大部分呢,因为事件不单单只有这两部分,还有其他的例如document的load和unloaded。那么事件在浏览器中,到底包含哪些信息呢?

  事件被封装成一个event对象,包含了该事件发生时的所有相关信息(event的属性)以及可以对事件进行的操作(event的方法)。

  我为下图中的button绑定了一个点击事件,然后将event输出到控制台:

 

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

  可以看到是一个KeyboardEvent对象,属性跟上面的也不太一样,如没有clientX/Y(敲键盘怎么能获取到鼠标的位置呢)。不管是MouseEvent还是KeyboardEvent或是其他类型,都是继承自一个叫Event的类。

  event对象常用属性、方法:

  1. 事件定位相关属性

  如果你细细看了MouseEvent对象里的属性,一定发现了有很多带X/Y的属性,它们都和事件的位置相关。具体包括:x/y、clientX/clientY、pageX/pageY、screenX/screenY、layerX/layerY、offsetX/offsetY 六对。为什么有这么多X-Y啊?不要着急,作为一个web开发者,你应该了解各浏览器之间是有差异的,这些属性都有各自的意思:

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

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

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

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

 

 下面列出了各属性的浏览器支持情况。(+支持,-不支持)    

offsetX/offsetYW3C- IE+ Firefox-Opera+ Safari+chrome+