事件
javascript与HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察者模式,支持页面的行为与页面的外观之间的松散耦合。
事件流
事件流描述的是从页面中接收事件的顺序。IE的事件流是事件冒泡流,Netscape的事件流是捕获流。
事件冒泡
event bubbling,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。以下HTML为例:
<html> <head> <title> event bubbling example </title> </head> <body> <div id="myDiv"> click me </div> </body> </html>
如果点击了页面中的div元素,那么这个click事件会按照如下顺序传播:
<div> <body> <html> document也就是说,click事件首先在div元素上发生,而这个元素就是我们单机的元素,然后click事件沿DOM树向上传播,在每一级节点上都会发生,直到传播到document对象。
事件捕获
event capturing,事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。仍以上面的HTML为例,那么单击div元素就会以下列顺序触发click事件:
document html body div在事件捕获过程中,document对象首先接收到click事件,然后事件沿DOM树依次向下,一直传播到事件的实际目标。
DOM事件流
DOM2级事件规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会,然后就是实际的目标接收到事件,最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应。
在DOM事件流中,实际的目标在捕获阶段不会接收到事件,这意味着在捕获阶段,事件从document到html再到body后就停止了,下一个阶段是“处于目标”,于是事件在div上发生,并在事件处理中被看成冒泡阶段的一部分,然后,冒泡阶段发生,事件又传播回文档。
Opera,Firefox,Chrome,safari都支持dom事件流,但是IE不支持DOM事件流。
事件处理程序
事件就是用户或者浏览器自身执行的某种动作(如click或mouseover等),而相应某个事件的函数就是事件处理程序(或事件侦听器)。
HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应的事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的javascript代码。例如,
<input type="button" value="click" οnclick="alert('clicked me')" />
或者使用页面外部定义的脚本或者引用外部文件中已定义好的函数。
<script type="text/javascript"> function showMessage() { alert("click me"); } </script> <input type="button" value="click" οnclick="showMessage" />
不过在HTML中指定事件处理程序有两个缺点。首先,存在一个时差问题。其次是HTML和javascript代码紧密耦合。
DOM0级处理程序
将一个函数赋值给一个事件处理程序属性,具有简单和跨浏览器的优势。
每个元素都有自己的事件处理程序属性,通常全部小写,例如:
var btn = document.getElementById('divId'); btn.onclick = function() { alert('click'); }
通过DOM0级方法指定的事件处理程序被认为是元素的方法,运行在元素的作用域内,程序中的this引用当前元素。
删除通过DOM0级方法指定的事件处理程序,只需给事件属性赋值为null即可,如下:
btn.onclick = null
DOM2级处理程序
DOM2级定义了2个方法,用于处理指定和删除事件处理程序的操作:addEventListener和removeEventListener。所有DOM节点都包含这两个方法,并且接受三个参数:要处理的事件名,作为事件处理的函数和一个boolean值。最后一个参数如果是true表示在捕获阶段调用事件处理程序;false表示在冒泡阶段调用事件处理程序。示例代码如下:
var btn = document.getElementById('buttonId'); btn.addEventListener('click',function() { alert('dom add event'); },false);
通过addEventListener添加的事件只能使用removeEvnetLisenter来移除,传入的参数和add方法一样。这意味着用addEventListener添加的匿名函数将无法移除。
var btn = document.getElementById('buttonId'); var handler = function() { alert(this.id); }; btn.addEventListener('click',handler,false); btn.removeEventListener('click',handler,false);
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度的兼容各大浏览器。
IE事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent和detachEvent,这两个方法接受相同的参数:事件处理程序名称和事件处理程序函数。由于IE只支持事件冒泡,所以通过attachEvent添加的事件处理程序都会被添加在事件冒泡阶段。示例如下:
var btn = document.getElementById('buttonId'); var handler = function() { alert(this.id); }; //添加click事件 btn.attachEvent('onclick',handler); //移除click事件 btn.detachEvent('onclick',handler);
在使用DOM0级方法时,事件处理程序会在其所属元素的作用域内运行,但是在使用attachEvent方法添加的事件处理程序会在全局域中运行,即此刻的this==window。
跨浏览器的事件处理程序
为了以跨浏览器方式处理事件,保证事件处理程序能够在大多数的浏览器中一致的运行,需要使用能力检测方式编写代码。
var EventUtil = { addHandler: function(element,type,handler) { if (element.addEventListener) { element.addEventListener(type,handler,false); }else if (element.attachEvent){ element.attachEvent(type,handler); }else { element['on' + type] = handler; } }, removeHandler: function(element,type,handler) { if (element.removeEventListener) { element.removeEventListener(type,handler,false); } else if(element.detachEvent) { element.detachEvent(type,handler); } else { element['on' + type] = null; } } }
实例代码:
var btn = document.getElementById('buttonId'); var handler = function() { alert("clicked me"); }; EventUtil.addHandler(btn,'click',handler); //移除事件 EventUtil.removeHandler(btn,'click',handler);
事件对象
在触发DOM上的某个事件时,会产生一个事件对象EVENT,这个对象中包含着所有与事件有关的信息。包括导致事件的元素,事件的类型,以及其他与特定事件相关的信息。
DOM中的事件对象
兼容DOM的浏览器都会将一个event对象传入到事件处理程序中,无论指定事件处理程序时使用什么方法,都会传入event对象。
var btn = document.getElementById('buttonId'); btn.οnclick= function(event) { alert(event.type); //click }; btn.addEventListener('click',function(event) { alert(event.type);//click },false);
event对象属性和方法
event对象属性和方法 属性和方法 类型 读/写 描述 bubbles Boolean 只读 表明事件是否冒泡 cancellable Boolean 只读 表明是否可以取消事件的默认行为 currentTarget Element 只读 事件处理程序当前正在处理事件的那个元素 eventPhase Integer 只读 调用事件处理程序的阶段 preventDefault Function 只读 取消事件的默认行为 stopPropagation Function 只读 取消事件的进一步捕获或冒泡 target Element 只读 事件的目标 type String 只读 被触发的事件的类型 detail Integer 只读 与事件相关的细节信息 在事件程序处理内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。
要阻止特地给事件的默认行为,可以使用preventDefault方法。例如阻止链接导航的默认行为
link.onclick =function(event) { event.preventDefault(); }
只有cancelable属性设置为true的事件,才可以使用preventDefault来取消其默认的行为。
eventPhase:确定事件当前正处于事件流的哪个阶段。1:捕获阶段;2:处于目标;3:冒泡阶段。
IE中的事件对象
IE中的event对象有几种形式,取决于指定事件处理程序的方法。在使用DOM0级方法添加事件处理程序,event对象作为window对象的一个属性存在,event = window.event;如果是使用attachEvent方法添加的事件处理程序,那么event对象作为参数被传入到事件处理函数中,和dom方式保持一致。
IE中的event对象和属性 属性/方法 类型 读写 描述 cancelbubble Boolean 读/写 默认值为false,true表示可以取消事件冒泡(与dom中的stopPropagation作用相同) returnValue Boolean 读/写 默认值为true,false表示取消事件的默认行为(与dom中的preventDefault作用相同) srcElement Element 只读 事件的目标(与dom中的target相同) type String 只读 被触发的事件类型 returnValue:作用是取消事件的默认行为,只要将其设置为false就可以阻止默认行为
var link = document.getElementById('linkId'); link.onclick =function(event) { event.returnValue = false; }
跨浏览器的事件对象
虽然DOM和IE中的event对象不同,但是基于他们之间的相似性质可以拿到跨浏览器的方案。IE中的event的全部信息和方法DOM对象中都有,只是实现方式不一样而已。
var EventUtil = { addHandler: function(element,type,handler) { if (element.addEventListener) { element.addEventListener(type,handler,false); }else if (element.attachEvent){ element.attachEvent(type,handler); }else { element['on' + type] = handler; } }, getEvent: function(event) { return event = event :window.event; }, getTarget : function(event) { return event.target || event.srcElement; }, preventDefault : function(event) { if(event.preventDefault) { event.preventDefault() } else { event.returnValue = false; } }, removeHandler: function(element,type,handler) { if (element.removeEventListener) { element.removeEventListener(type,handler,false); } else if(element.detachEvent) { element.detachEvent(type,handler); } else { element['on' + type] = null; } }, stopPropagation: function(event) { if (event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } } }
待续。