JS中DOM0,DOM2,DOM3级事件模型解析

声明:本文来源于http://www.webzsky.com/?p=731
我只是在这里作为自己的学习笔记整理一下(原文格式看的我眼睛疼)

javascript中的事件

javascript程序使用的是事件驱动的设计模式,为一个元素添加事件监听函数,当这个元素的相应事件被触发那么其添加的事件监听函数就被调用:事件是javascript和HTML交互基础, 任何文档或者浏览器窗口发生的交互, 都要通过绑定事件进行交互; 所有浏览器都支持DOM0级事件处理程序,且使用该方式时,事件处理程序是在元素的作用域中运行,因此程序中的this都是指向元素。

如何理解DOM0,DOM2,DOM3

文档对象模型是一种与编程语言及平台无关的API(Application programming Interface),借助于它,程序能够动态地访问和修改文档内容、结构或显示样式。
W3C协会早在1988年就开始了DOM标准的制定,W3C DOM标准可以分为DOM1,DOM2,DOM3三个版本。

DOM1级

主要定义的是HTML和XML文档的底层结构。DOM2和DOM3级别则在这个结构的基础上引入了更多的交互能力,也支持了更高级的XML特性。为此DOM2和DOM3级分为许多模块(模块之间具有某种关联),分别描述了DOM的某个非常具体的子集。

这些模块如下:
  1. DOM2级核心(DOM Level 2 Core):在1级核心的基础上构建,为节点添加了更多方法和属性;
  2. DOM2级视图(DOM Level 2 Views):为文档定义了基于样式信息的不同视图;
  3. DOM2级事件(DOM Level 2 Style):定义了如何以编程方式来访问和改变CSS样式信息;
  4. DOM2级遍历和范围(DOM Level 2 Traversal and Range):引入了遍历DOM文档和选择其特定部分的新接口。
  5. DOM2级HTML(DOM Level 2 HTML):在1级HTML基础上构建,添加了更多属性、方法和新接口。
  6. DOM3级又增加了XPath模块和加载与保存(Load and Save)模块。
    DOM2级和3级的目的在于扩展DOM API,以满足操作XML的所有需求,同时提供更好的错误处理及特性检测能力。 DOM0就是直接通过 onclick写在html里面的事件; DOM2是通过addEventListener绑定的事件, 还有IE下的DOM2事件通过attachEvent绑定; DOM3是一些新的事件。

0级DOM:

一开始浏览器处理事件的时候只有原始事件模型,事件处理程序被设置为js代码串作为html的性质值,例如:
<input id="myButton" type="button" value="Press Me" onclick="alert('thanks');" >
在js中html元素都有一个对应的对象,这个对象的属性对应那个html元素的性质,所以可以用js代码添加事件监听函数
document.getElementById("myButton").onclick = function () {alert('thanks');}
通常情况下事件监听函数如果返回一个值并且是false,则会阻止浏览器执行默认的动作。无论用html还是js,都是把一个函数赋值给文档元素,在事件监听函数被调用时候它是作为产生事件的元素的放法调用的,所以this引用的是那个目标元素(例子中的Input对象)。从技术上来说,W3C的DOM标准并不支持上述最原始的添加事件监听函数的方式,这些都是在DOM标准形成前的事件模型。尽管没有正式的W3C标准,但这种事件模型仍然得到广泛应用,这就是我们通常所说的0级DOM。

2级DOM

DOM级别1于1998年10月1日成为W3C推荐标准。1级DOM标准中并没有定义事件相关的内容,所以没有所谓的1级DOM事件模型。在2级DOM中除了定义了一些DOM相关的操作之外还定义了一个事件模型 ,这个标准下的事件模型就是我们所说的2级DOM事件模型2级DOM的事件传播在2级DOM中,当事件发生在节点时,目标元素的事件处理函数就被触发,而且目标的每个祖先节点也有机会处理那个事件。

因为2级DOM的事件传播分三个阶段进行。

1.capturing阶段,事件从Document对象沿着文档树向下传播给节点。如果目标的任何一个祖先专门注册了事件监听函数,那么在事件传播的过程中就会运行这些函数。
2.下一个阶段发生在目标节点自身,直接注册在目标上的适合的事件监听函数将运行。
3.是bubbling阶段,这个阶段事件将从目标元素向上传播回Document对象(与capturing相反的阶段)。虽然所有事件都受capturing阶段的支配,但并不是所有类型的事件都bubbling。(0级DOM事件模型处理没有capturing阶段)

事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括事件的元素、事件的类型,以及其他与特定事件相关的信息。例如,鼠标操作事件,包含鼠标的位置信息,键盘操作事件包含按下的键的信息。所有浏览器都支持event对象,但支持方式不同。
[IE的事件流是冒泡 从里面往上面冒, netscape是从外部元素往内部元素捕获; 而DOM2级的事件规定了事件流包含三个阶段包括:1:事件捕获 2:处于目标阶段 3:事件冒泡阶段 (IE8以及更早版本不支持DOM事件流); 无论在DOM0还是DOM2还是DOM3中都会在事件函数中传入事件对象;
<script type="text/[javascript] var p = document.getElementById('p'); p.addEventListener("click",function(){ console.log(arguments[0]); }) </script>
点击a后capturing阶段事件传播会从document-> span->a,然后发生在a,最后bubbling阶段事件传播会从a->span->document 。2级DOM的事件监听函数注册
2级事件模型中,可以调用对象的addEventListener()方法为元素设置事件监听函数,也就是说通过2级DOM的这个API注册的函数才有可能在上述事件传播三个阶段中任意一个阶段捕捉到事件的发生(如果用0级DOM的2个方法赋值的事件监听函数不能在capturing阶段捕捉到事件)。
1 .addEventListener第一个参数是String,事件类型名,没有前缀on,比如要注册click事件就传入“click”,不是“onclick”2 .第二个参数是监听函数,在调用的时候js会传给他一个Event对象,这个对象放了有关事件的细节,如果调用的这个对象的stopPropagation()方法,则会阻止事件传播进一步传播(比如在第一个阶段捕捉到事件并运行事件监听函数,其中调用了event。stopPropagation则事件就不会再被传播经历第二第三阶段了)3 .第三个参数是boolean,true表示事件监听函数能够在三个阶段中的任意一个阶段捕捉到事件(符合2级DOM标准),如果是false就表示事件监听函数不能在capturing阶段捕捉到事件(表现同0级DOM)。2级DOM中监听函数中的this
通过addEventListener添加的函数中的this,标准中并没有规定this必须指向目标元素, 尽管大多数浏览器都是这么实现的,但最终还是取决于浏览器的实现,我们需要用到目标元素的时候请调用event.currentTarget.

2级DOM的Event对象

用addEventListener添加的事件监听函数,在被调用的时候js会传给他一个Event对象,下面就是这个Event对象的常用属性type:发生的事件的类型,例如"click", "mouseover"target:发生事件的节点,可能与currentTarget不同currentTarget:正在处理事件的节点,如果在capturing阶段和冒泡阶段处理事件,这个属性就与target属性不同。在事件监听函数中应该用这个属性而不是thisstopPropagation():可以阻止事件从当前正在处理他的节点传播preventDefault():阻止浏览器执行与事件相关的默认动作,与0级DOM中返回false一样clientX, clientY:鼠标相对于浏览器的x坐标y坐标screenX, screenY:鼠标相对于显示器左上角的x坐标y坐标

IE事件模型

1.Event对象不是传递给事件监听函数,而是通过window对象的event属性访问Event对象.
2.IE Event对象常用属性type:
兼容DOM的type属性srcElement:
兼容DOM的target属性clientX, clientY:
兼容DOM的clientX, clientY属性cancelBubble:布尔值,设为true同调用stopPropagation()returnValue:布尔值,设为false同调用preventDefault()3.事件监听函数注册没有addEventListener,只有attachEvent。2个参数,同addEventListener前两个,只是事件名带前缀on。 
IE事件模型没有capturing阶段所以调用attachEvent相当于调用addEvetnListener且第三个参数为false:
document.getElementById("myTest").attachEvent("onclick", function(){alert(1)});
相当于
document.getElementById("myTest").addEventListener("click", function(){alert(1)}, false);
4.用attachEvent注册的函数将被作为全局函数调用,而不是作为发生事件的文档元素的方法,也就是说this引用的是window对象,而不是事件的目标元素。

事件对象event下的属性和方法

因为各个浏览器的事件对象不一样, 把主要的事件对象的属性和方法列出来;

  • bubble : 表明事件是否冒泡
  • cancelable : 表明是否可以取消冒泡
  • currentTarget : 当前事件程序正在处理的元素, 和this一样的;
  • defaultPrevented: false ,如果调用了preventDefualt这个就为真了;
  • detail: 与事件有关的信息(滚动事件等等)
  • eventPhase: 值为1表示处于捕获阶段, 值为2表示处于目标阶段,值为3表示在冒泡阶段
  • target || srcElement: 事件的目标
  • trusted: 为ture是浏览器生成的,为false是开发人员创建的(DOM3)
  • type : 事件的类型 view : 与元素关联的window, 我们可能跨iframe;
  • preventDefault() 取消默认事件;
  • stopPropagation() 取消冒泡或者捕获;
  • stopImmediatePropagation() (DOM3)阻止任何事件的运行; stopImmediatePropagation阻止绑定在事件触发元素的 其他同类事件的callback的运行 IE下的事件对象是在window下的,而标准应该作为一个参数, 传为函数第一个参数; IE的事件对象定义的属性跟标准的不同,如: cancelBubble 默认为false, 如果为true就是取消事件冒泡; returnValue 默认是true,如果为false就取消默认事件; srcElement, 这个指的是target, Firefox下的也是srcElement;

事件监听** (Event Listeners,包括 addEventListener 以及 IE 的 attacheEvent)**

旧版本的 IE 在执行 Javascript 时与几乎所有其它浏览器不同,在 IE 9 之前的版本中,你需要使用 attachEvent 模块,就像这样:
element.attachEvent('onclick', function() { /* do stuff here*/ });
在大部分其它浏览器(包括 IE 9 以及更新的版本)中,你可以使用 addEventListener,就像这样:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
使用这些方法(DOM2 事件),理论上你可以向某个元素加入无数的事件。但实际上,这会受限于客户端的内存容量以及其它的性能问题,而这对于每一个浏览器都是不同的。
上面的例子使用的都是匿名函数,你也可以将一个函数表达式或者一个闭包添加到事件监听上:
var myFunctionReference = function(){ /* do stuff here */ } element.attachEvent('onclick', myFunctionReference);element.addEventListener('click', myFUnctionReference, false);
addEventListener 还有一个特点就是最后的参数,它会控制监听在事件冒泡阶段时就作出反应。有大约 95% 的可能会像我在例子中那样使用 false。这个参数在 attachEvent 中或在使用内联事件(inline Events) 时没有等效的参数。

内联事件 (Inline Events, 即 HTML 中的onclick=""属性和element.onclick)

在所有支持 Javascript 的浏览器中,你可以将一个事件监听内联,也就是像下面的 HTML 代码那样:
<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>
虽然它的确是可以完成任务的,而且简单直接,但绝大部分有经验的开发者都会尽量避开使用这样的方法。同时,你不能在这里使用闭包或者匿名函数(虽然处理程序本身就是一个匿名函数),而且你的控制范围是有限的。另一个方法是这样的
element.onclick = funtion () { /* do stuff here / }
实际上这等价于内联 Javascript (也就是上面那种在 HTML 标签属性中添加的方法),不过这样可以拥有更大的控制范围,同时可以使用匿名函数、函数表达式或闭包。
内联事件有个重大的缺点就是,不像上面提到的事件监斩器那样,你只可以指定一个内联事件。内联事件会转化元元素的属性,那意味着当指定多个的内联事件时,它之前所指定的内联事件会被覆盖掉。
使用上面 HTML 代码中的 标签来举个例子:
var element = document.getElementById('testing'); element.onclick = function () { alert('did stuff #1'); }; element.onclick = function () { alert('did stuff #2'); };
当你点击这个元素后,你只可以看到 "Did stuff #2",原因是第二个值覆盖了第一个指定的 onclick 属性,同时,会把 HTML 中 onclick 属性也覆盖掉。
二者谁更好呢?*
主要的问题是浏览器兼容性和必要性。你目前是否需要添加一个以上的事件到一个元素上?未来是否需要?大部分时候,你是需要的。所以,使用 attachEvent 和 addEventListener 是非常有必要的,不然用内联事件就好了。
JQuery 以及很多其它的 Javascript 框架都为不同的浏览器封装了通用的处理 DOM2 事件的通用模型(Models),这样你可以在做跨浏览器兼容时不需要为 IE 的历史遗留问题而烦恼了。同样的代码在 jQuery 中做跨浏览器兼容,只需要这样:
$(element).on('click', function () { /* do stuff */ });
当然了,不要因为这么一件事而使用一个框架。你可以很容易地写出一个小工具来兼容旧版本的浏览器:
function addEvent(element, evnt, funct){if (element.attachEvent)return element.attachEvent('on' + evnt, funct);elsereturn elemt.addEventListener(evnt, funct, false);} //exampleaddEvent(document.getElementById('myElement'),'click',function () { aler('hi!'); });

事件处理程序

1、HTML事件处理程序某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的JavaScript代码。

HTML中指定事件处理程序有两个缺点。一个是时差问题。二个是HTML与JavaScript代码紧密耦合(如果要更换事件处理程序,就要改动两个地方:HTML代码和JavaScript代码)。

2、DOM0级事件处理程序要使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。

每个元素(包括window和document)都有自己的事件处理程序属性,这些属性通常全部小写,例如onclick。将这种属性的值设置为一个函数,就可以指定事件处理程序:
var btn = document.getElementByIdx_x("myBtn"); btn.onclick = function(){ alert("Clicked"); }
使用DOM0级方法指定的事件处理程序被认为是元素的方法。换句话说,程序中的this引用当前元素。例如:
var btn = document.getElementByIdx_x("myBtn"); btn.onclick = function(){ alert("this.id"); }
删除DOM0级事件处理程序,只需这样:btn.onclick = null; //删除事件处理程序将事件处理程序设置为null后,单击按钮将不会有任何动作发生。如果你使用HTML指定事件处理程序,那么onclick属性的值就是一个包含着在同名HTML特性中的指定的代码的函数。而将相应的属性设置为null,也可以删除以这种方式指定的事件处理函数。

3、DOM2级事件处理程序(IE不支持)**DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。

var btn = document.getElementByIdx_x("myBtn"); btn.addEventListener("click",function(){alert("this.id");},false);
使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。例如:
var btn = document.getElementByIdx_x("myBtn"); btn.addEventListener("click",function(){alert("this.id");},false); btn.addEventListener("click",function(){alert("hello world");},false);
删除事件应用程序。例如:
var btn = document.getElementByIdx_x("myBtn"); var handler = function(){ alert(this.id); } btn.addEventListener("click",handler,false); //省略了其他代码 btn.removeEventListener("click",handler,false);//有效!
注:匿名函数无法删除。
4、IE事件处理程序 IE实现了与DOM中类似的方法:attachEvent()和detachEvent()**
例如:
var btn = document.getElementByIdx_x("myBtn"); btn.attachEvent("onclick",function(){alert("this.id");});
注意:attachEvent()的第一个参数是onclick,而非click。同时,这里的this不在是作用域内的元素,而是window对象。 同理,删除事件处理函数用detachEvent()函数。

事件类型

1、DOM2级事件类型
(1)UI(user interface,用户界面)事件,在用户与页面上的元素交互时触发。      
(2)鼠标事件,当用户通过鼠标在页面上执行操作是触发。
(3)键盘事件,当用户通过键盘在页面上执行操作是触发。
(4)HTML事件,当浏览器窗口发生变化或发生特定的客户端/服务器交互时触发。
(5)变动(mutation)事件,当底层DOM结构发生变化时触发。

2、UI事件
(1)DOMActive:表示元素已经被用户操作(通过鼠标或键盘)激活。
(2)DOMFocusIn:表示元素已经获得了焦点;
(3)DOMFocousOut:表示元素已经失去了焦点。支持这几个UI事件的浏览器很少,
因此我们不推荐使用。
3、鼠标事件
(1)鼠标事件触发顺序:mousedown-mouseup-click-mousedown-mouseup-click-dbclick。
(2)客户区坐标位置
var div=docment.getElementById("myDiv");EventUtil.addHandler(div,"click",function(event) {event = EventUtil.getEvent(event);alert("Client coordinates:"+event.clientX+","+event.clientY);});
(3)屏幕坐标位置
var div = document.getElementByIdx_x("myDiv");EventUtil.addHandler(div, "click", function(event){event = EventUtil.getEvent(event);alert("Screen coordinates: " + event.screenX + "," + event.screenY);});
4、键盘事件(DOM0级事件支持)
(1)键盘事件包括三个:keydown 、 keypress 、 keyup。
(2)键码,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键对应。对应的键码请查表。
5、HTML事件load、unload、abort、error、select、change、submit、reset、resize、scroll、focus、blur。多数HTML事件都与window对象或表单控件相关。
6、变动事件(略)

推荐阅读更多精彩内容