事件是文档或浏览器窗口中发生的一些特定的交互瞬间。
事件流
事件流描述的是从页面中接受事件的顺序。而IE和Netscape开发团队提出了完全相反的两个事件流的概念。IE的事件流是事件冒泡流;Netscape的事件流是事件捕获流。
事件冒泡:即事件最开始由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播至最不具体的那个节点(文档)。
事件捕获:不太具体的节点应该更早接收到事件,而最具体的节点最后接收到事件。
使用事件处理程序
◆ HTML事件处理程序
事件是直接加在html结构里的html元素上的(不推荐,因为违反了HTML与JavaScript分离的准则)
HTML事件的缺点:HTML和JS代码紧密的偶合在一起,如果要更换已经写好的事件处理程序,就需要手动改两个地方(JS代码和HTML都需要修改),很不方便。
◆ DOM0级事件处理程序
javascript指定处理程序比较传统的一种方式,即把一个函数赋值给一个事件的处理程序属性。是目前用的比较多的方法。所有浏览器都支持DOM0级事件处理程序,且使用该方式时,事件处理程序是在元素的作用域中运行,因此程序中的this都是指向元素。
DOM0级事件的优点:简单、跨浏览器的优势。可以给一个元素添加多个事件。
DOM0级事件的缺点:虽然可以给一个元素添加多个事件,但一个事件只能绑定一个响应函数,重复绑定会覆盖之前的绑定。
如果要删除某个事件处理程序,直接将指定元素的事件属性设置为null即可。
◆ DOM2级事件处理程序
比较推荐使用,可以给一个元素添加多个事件并绑定多个不同的函数,遗憾的是,它不兼容IE浏览器。
DOM2级事件定义了两个方法,分别用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。
(1).addEventListener(event,function,useCapture)接收三个参数:要处理的事件名、作为事件处理程序的函数、布尔值。
▶ event:字符串,事件名称,如'click'等 不需要'on'前缀。(必须)
▶ function:事件处理的函数,实现EventListener接口。(必须)
(1).可以附加多个事件处理函数,执行顺序按照绑定的先后
(2).关于处理函数删除的问题,使用addEventListener()将事件处理函数加入到捕获阶段,则必须在removeEventListener()中指明是捕获阶段,才能正确地删除这个事件处理函数。
(3).使用传统的方法(DOM0级事件处理程序)直接给事件处理函数属性赋值,事件处理函数将被添加到事件的冒泡阶段。
▶ useCapture:是否使用捕获--- true表示该元素在事件的“捕获阶段”(由外往内传递时)响应事件,false表示该元素在事件的“冒泡阶段”(由内向外传递时)响应事件。(捕获过程要先于冒泡过程)(可选。默认为false)
当鼠标点击所看到的按钮时,其实发生了一系列的时间传递,可以想象一下,button实际上是被body"包裹"起来的,body是被html"包裹"起来的,html是被document"包裹"起来的,document是被window“包裹”起来的。所以,在你的鼠标点下去的时候,最先获得这个点击的是最外面的window,然后经过一系列传递才会传到最后的目标button,当传到button的时候,这个事件又会像水底的泡泡一样慢慢往外层穿出,直到window结束。
综上,一个事件的传递过程包含三个阶段,分别称为:捕获阶段,目标阶段,冒泡阶段。目标指的就是包裹得最深的那个元素。
因为在大多数情况下我们都是将事件处理程序添加到事件的冒泡阶段,这样可以最大限度的兼容各大浏览器,所以一般情况下都将useCapture的值设为false。
(2).removeEventListener(event,function,useCapture)接收三个参数:要删除的事件名、要移除的函数、布尔值(指定移除事件句柄的阶段)。
通过addEVentListener添加的事件只能通过removeEventListener删除。
◆ IE事件处理程序
IE有自己的一套事件处理程序,它提供了两个方法,分别用于处理指定和删除事件处理程序的操作:attachEvent(event,function)和detachEvent(event,function)。(IE11已不再支持该方法,只兼容DOM0级事件处理程序)
▶ event:字符串,事件名称,如'onclick'等 需要加上'on'前缀。(必须)
▶ function:事件处理的函数。(必须)
这两个方法接收相同的两个参数:事件处理程序名称和事件处理程序函数。由于IE8以及更早的浏览器版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域。在使用DOM0级的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window;IE下,用attachEvent()方法指定多个事件处理程序,不是以添加它们的顺序执行,而是以相反的顺序被触发。
◆ 跨浏览器的事件处理程序
根据不同的浏览器支持的事件处理程序的不同,这里将事件处理程序封装在了一个单独的js文件中(eventUtil.js),根据能力检测法,分别判断当浏览器支持DOM2级、IE事件处理程序、DOM0级时执行的事件处理程序。
eventUtil.js
//跨浏览器事件处理程序
var eventUtil={
//添加句柄(element:元素,type:事件类型,handler:事件处理函数)
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{ //DOM0级事件处理程序的判断
element['on'+type]=handler;
}
},
//删除句柄(element:元素,type:事件类型,handler:事件处理函数)
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{ //DOM0级事件处理程序的判断
element['on'+type]=null;
}
}
};
这里说明一下,上面代码中element['on'+type]=handler;由于我们在用DOM0级向元素添加事件时,都是用诸如element.onclick的方式,为什么这里不能用element.'on'+type这种语法呢?记住,这个地方是不能用'.'去连接变量和字符串的,所以这种语法是错误的。在js中访问一个属性时,所有用'.'的地方都可以用'[]'。也就是说:element.onclick与element["onclick"]是完全等价的,所以这个地方可以用element['on'+type]的方式访问元素属性。
事件对象
什么是事件对象?在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型,以及其它与特定事件相关的信息。例如,鼠标操作导致的事件对象中,会包含鼠标位置的信息,而键盘操作导致的事件对象中,会包含与按下的键有关的信息。所有浏览器都支持event对象,但支持方式不同。
◆ DOM事件对象
兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序时使用DOM0级或是DOM2级,都会传入event对象。在通过HTML特性指定事件处理程序时,变量event中保存着event对象。例:<input type="button" value="" onclick="alert(event.type)" />。event对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。不过,所有事件都会有下表列出的成员。
属性/方法 | 类型 | 读/写 | 说明 |
bubbles | Boolean | 只读 | 表明事件是否冒泡 |
cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为 |
currentTarget | Element | 只读 | 其事件处理程序当前正在处理事件的那个元素 |
detail | Integer | 只读 | 与事件相关的细节信息 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段 |
preventDefault() | Function | 只读 | 取消事件的默认行为。如果cancelable为true, 则可以使用这个方法 |
stopPropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法 |
target | Element | 只读 | 事件的目标 |
type | String | 只读 | 被触发的事件的类型 |
view | AbstractView | 只读 | 与事件关联的抽象视图。等同于发生事件的window对象 |
在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含时间段额实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget、和target包含相同的值。
事件对象event中比较重要的两个属性:
▶.type属性:用于获取事件类型
▶.target属性:用于获取事件目标
事件对象event中比较重要的两个方法:
▶.stopPropagation()方法:用于阻止事件冒泡
<body>
<div id="box">
<input type="button" value="按钮" id="btn"/>
</div>
<script src="eventUtil.js"></script>
<script>
function showMessage(){
alert("点击了btn");
}
function showBox(){
alert("点击了box");
}
var box=document.getElementById("box");
var btn=document.getElementById("btn");
eventUtil.addHandler(box,'click',showBox);
eventUtil.addHandler(btn,'click',showMessage);
</script>
</body>
运行上面代码,点击“按钮”时会发现,浏览器弹出"点击了btn"后,随后接着弹出"点击了box",事件冒泡会认为我们点击了按钮的同时,也点击了它的父元素div,一直向上触发到整个document乃至window。这种情况有时候我们在开发中并不想看到,点击按钮时就是点击按钮,点击div时就是点击div,互不干扰才行。所以这里需要阻止事件冒泡,使每个事件处理程序都不被影响。(当然有些特殊情况下还是需要冒泡的)
这样,再点击按钮的时候就不会冒泡到div了,点击div也不会接着往上冒泡。
▶.preventDefault()方法:用于阻止事件的默认行为
比如a标签<ahref="#">超链接</a>,有一个默认的行为就是跳转页面,那么以a标签为例,取消它的默认跳转行为。
运行上面代码,a标签将不再跳转。
◆IE中的事件对象
IE浏览器中的event和非IE浏览器的event也是有区别的,在非IE浏览器中可以直接用event,它就是简单的传入传出的过程;但是在IE尤其是IE8以下的版本中,引用事件对象是是要通过window.event引用的。所以为了更好地兼容,在使用事件对象event'时,最好写成:event=event || window.event;
IE的event对象同样也包含着与创建它的事件相关的属性和方法。这些属性和方法也会因为事件类型的不同而不同,但所有事件对象都会包含下表所列的属性和方法。
属性/方法 | 类型 | 读/写 | 说明 |
cancelBubble | Boolean | 读/写 | 默认为false,但将其设置为true就可以取消事件冒泡 |
returnValue | Boolean | 读/写 | 默认为true,但将其设置为false就可以取消事件的默认行为 |
srcElement | Element | 只读 | 事件的目标 |
type | String | 只读 | 被触发的事件的类型 |
▶ type属性:用于获取事件类型
▶ srcElement属性:用于获取事件的目标
var ele=event.traget || event.srcElement; //获取事件目标,兼容IE浏览器和非IE浏览器
▶ cancelBubble属性:用于阻止事件冒泡
设置为true表示阻止冒泡,设置为false表示不阻止冒泡。
▶ returnValue属性:用于阻止事件的默认行为
设置为false表示阻止事件的默认行为
◆ 跨浏览器的事件对象
同上面跨浏览器的事件处理程序一样,根据不同的浏览器支持的事件对象的不同,这里将事件对象相关操作封装在了一个单独的js文件中(eventUtil.js),根据能力检测法,分别判断当浏览器支持DOM对象、IE事件对象时采用的事件对象。
完整的eventUtil.js//跨浏览器事件处理程序
var eventUtil={
//添加句柄(element:元素,type:事件类型,handler:事件处理函数)
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{ //DOM0级事件处理程序的判断
element['on'+type]=handler;
}
},
//删除句柄(element:元素,type:事件类型,handler:事件处理函数)
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{ //DOM0级事件处理程序的判断
element['on'+type]=null;
}
}
//获得事件对象
getEvent:function(){
return event?event:window.event;
},
//获得事件类型
getType:function(){
return event.type;
},
//获得事件目标元素
getElement:function(){
return event.target || event.srcElement;
}
//阻止默认行为
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},
//阻止事件冒泡
stopPropagation:function(event){
if(event.stopPropagation){ //以属性的形式进行判断
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
};