第12章 事件
1.事件流
1.1事件冒泡(ie事件流)
□事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接受,然后逐级向上传播到较为不具体的节点(文档)。
□所有浏览器均支持事件冒泡。firefox、chrome、safari将事件一直冒泡到window对象。
1.2事件捕获(netscape事件流)
□不太具体的节点更早收到事件,而具体的节点最后收到节点。
□safari、chrome、opera、firefox支持,但从window对象开始捕获(dom2级规范是从document开始)。
1.3dom事件流
□“dom2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
□dom2:捕获阶段不含实际目标,不涉及事件目标,只为截获事件提供机会。
□safari、chrome、firefox和opera9.5以上都会在捕获阶段触发事件对象上的事件。结果有两个机会在目标上操作事件。
□ie不支持dom事件流。其他支持。
2.事件处理程序(或事件侦听器)
定义:响应某个事件的函数。
2.1 html事件处理程序
□某个元素支持的每种事件,都可以使用一个相应事件处理程序同名的html特性来指定。
□其中的函数代码字符需经过html转义。
□存在问题:
◇时差问题:当触发事件时,事件处理程序有可能尚不具执行条件。使用try-catch块封装,以便错误不会浮出水面。
◇html与javascript代码紧密耦合。
2.2 dom0级事件处理程序
□dom0级事件处理程序:将一个函数赋值给一个事件处理程序属性。优点:简单、跨浏览器。
□这种方式的事件处理程序在代码运行以前不会绑定事件。
□使用dom0级方法指定的事件处理程序被认为是元素的方法。事件处理程序在元素作用域中运行;程序中this引用当前元素。
□删除dom0级事:btn.onclick = null; //删除事件处理程序
2.3 dom2级事件处理程序
□addeventlistener()和removeeventlistener()。接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。
◇如果最后这个布尔值是true,表示捕获阶段调用事件处理程序;如果是false表示冒泡阶段调用事件处理程序。
var btn = document.getelementbyid(mybtn);
btn.addeventlistener(click,function(){alert(this.id);},false);
□使用dom2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。按添加顺序触发。
□通过addeventlistener()添加的事件处理程序只能使用removeeventlistener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。即匿名函数无法移除。
□将事件处理程序添加到冒泡阶段,得到最大限度兼容。
□firefox、safari、chrome和opera支持dom2级事件处理程序。ie不支持,有独有的事件处理程序。
2.4 ie事件处理程序。
□ie中与dom2类似方法:attachevent()和detachevent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。
□ie仅支持冒泡,通过attachevent()添加的事件处理程序都被添加到冒泡阶段。
□attachevent()中事件处理函数会再全局作用域中运行,因此this等于window。
□attachevent()也可以为元素添加多个处理程序,以相反的书序触发。
□attachevent()事件可通过detachevent()移除,匿名函数无法移除。
2.5 跨浏览器的事件处理程序
var eventutil = {
addhandler : function(element, type, handler){
if(element.addeventlistener){
element.addeventlistener(type,handler,false);
}else if(element.attachevent){
element.attachevent(on + type, handler);
}else{
elmenet[on + 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{
element[on+type] = null;
}
}
};
□此兼容函数没有考虑到浏览器的所有问题,如ie中作用域问题。
3.事件对象
定义:在触发dom的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。
3.1dom中的事件对象
①兼容dom的浏览器会将一个event对象传入到事件处理程序中(dom0级或dom2级)。通过html特性指定时,event变量保存event对象。
②所有event对象均包含以下属性/方法。p291
□bubbles:表明事件是否冒泡,bool。
□cancelable:表明是否可以取消事件的默认行为,bool。
□currenttarget:其事件处理程序当前正在处理事件的那个元素。
□detail:与事件相关的细节信息。
□eventphase:调用事件处理程序的阶段:1.捕获 2.处于目标 3.冒泡。
□preventdefault():取消事件默认行为。
□stoppropagation():取消事件的进一步捕获或冒泡。
□target:事件的目标。
□type:被触发的事件类型。
□view:与事件关联的抽象视图。
③在事件处理程序内部:
□对象this始终等于currenttarget的值,即this和currenttarget均指向绑定该事件程序的元素。
□target则只包含事件的实际目标,即触发事件的元素。
④event.preventdefault():可取消特定事件的默认行为。
⑤event.stoppropagation():立即停止事件在dom中的传播。
⑥通过eventphase属性确定事件当前正位于事件流哪个阶段。
3.2 ie中的事件对象
①访问ie中的event对象,取决于指定的事件处理程序方法。
□使用dom0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。
□使用attachevent()添加,则有一个event对象作为参数传入。同时也可以通过window.event访问。
□通过html特性指定事件处理程序时,可以通过一个名叫event的变量访问event对象。
②ie中所有的event对象包含以下属性/方法:
□cancelbubble:默认为false,设为true可取消事件冒泡(类似dom中的stoppropagation()方法)
□returnvalue:默认为true,设为false可取消事件默认行为。(类似dom2中的preventdefault()方法)
□srcelement:事件的目标(与dom2中target属性相同)
□type:被触发的事件类型。
3.3 跨浏览器的事件对象
var eventutil = {
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;
}
},
stoppropagation : function(event){
if(event.stoppropagation){
event.stoppropagation();
}else{
event.cancelbubble = true;
}
}
};
4.事件类型
□鼠标事件,当用户通过鼠标在页面上执行操作时触发;
□键盘事件,当用户通过键盘在页面上执行操作时触发;
□html事件,当浏览器窗口发生变化或发生特定的客户端/服务器交互时触发。
□变动(mutation)事件,当底层dom结构发生变化时触发。
4.1 ui事件
ui事件主要与元素焦点相关,仅在兼容dom的浏览器中受到支持:
□domactive:表示元素已经被用户操作(通过鼠标或键盘)激活;
□domfocusin:表示元素已经获得焦点;为html中focus事件的普通版;
□domfocusout:表示元素已经失去焦点;为html中blur事件的普通版;
△由于支持的浏览器很少,不推荐使用。
4.2 鼠标事件
①dom中定义了7个鼠标事件,页面上所有元素都支持鼠标事件:
□click:在用户单击主鼠标按钮(一般式左边的按钮)或者按下回车键时触发。
□dblclick:在用户双击主鼠标按钮(一般是左键)时触发。
□mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
□mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。新移入的元素可能在旧元素外部,也可以是其子元素。不能通过键盘触发。
□mouseover:在鼠标指标为于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不通过键盘触发。
□mouseup:在用户释放鼠标按钮时触发。不通过键盘触发。
□mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发。
②注意事项:
□同一个元素上相继触发mousedown和mouseup事件才会触发click事件。如果mousedown或mouseup其中之一取消,就不会触发click事件。
□同一元素连续两次触发click事件,才会触发dblclick事件。
4.2.1 客户区坐标位置
□鼠标事件都是在浏览器视口中的特定位置发生的。这个位置信息保存在事件对象(event)中的clientx和clienty属性中。
4.2.2 屏幕坐标位置
□鼠标事件发生时,还有一个相对于整个电脑屏幕的位置。保存在事件对象(event)中的screenx和screeny属性中。
4.2.3 修改键
□虽然鼠标事件主要是使用鼠标来触发,但按下鼠标时键盘上的某些键也可以影响到所需操作。
□修改键为:shift、ctrl、alt和meta(苹果中cmd键、windows中windows键)。
□dom中表示这4个键的布尔值属性:(在鼠标事件的event中)shiftkey、ctrlkey、altkey和metakey(ie不支持metakey)。
4.2.4 相关元素
发生mouseover和mouseout事件时,会涉及更多元素。两事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。
□dom通过evnet对象的relatedtarget属性提供了相关元素信息。
□ie不支持relatedtarget属性。
◇在mouserover事件触发时,ie的fromelement属性中保存了相关元素。
◇在mouseout事件触发时,ie的toelement属性保存了相关元素。
□兼容方式:
var eventutil = {
getrelatedtarget : function(event){
if(event.relatedtarget){
return event.relatedtarget;
}else if(event.toelement){
return event.toelement;
}else if(event.fromelement){
return event.fromelement;
}else{
return null;
}
}
};
4.2.5 鼠标按钮
□click事件:单击鼠标主键触发,无必要检测按键信息。
□mousedown和mouseup事件,在其event对象中有一button属性表示按下或释放的按钮。
□dom中的button属性取值:
◇0表示主鼠标按键
◇1鼠标中键(滚轮按钮)
◇2鼠标次键
□ie提供的button属性
◇0没按键◇1按主键◇2按次键◇3同时主+次◇4中键◇5主+中◇6次+中◇7主+次+中
□兼容代码
getbutton : function(event){
if(document.implementation.hasfeature(mouseevents,2.0)){
return event.button;
}else{
switch(event.button){
case 0 :
case 1 :
case 3 :
case 5 :
case 7 :
return 0;
case 2 :
case 6 :
return 2;
case 4 :
return 1;
}
}
}
};
□如果不是按下或释放主键,opera不会触发mouseup或mousedown
4.2.6 更多的事件信息
□dom2中detail属性:元素单击次数,离离开元素后清0.
□ie为鼠标增加更多信息。(仅ie支持,并无实用价值)。
4.2.7移动safari
面向iphone和ipod中safari开发时:
□不支持dblclick事件。双击safari窗口会放大画面,无法改变该行为。
□轻击可单击元素先触发mousemove事件,若导致内容变化则无其他事件发生。若无内容变化,一次发生mousedown、mouseup和click。
□mousemove事件也会触发mouseover和mouseout事件。
4.2.7 易访问性问题
屏幕阅读器只可通过click来触发事件。使用鼠标事件时应该注意:
□使用click事件执行代码
□不要使用onmouseover向用户显示新选项。屏幕阅读器无法触发。
□不要使用dblclick执行重要操作。键盘无法触发。
4.3 键盘事件
①对键盘事件的支持主要遵循dom0级。“dom3级”为键盘事件制定了规范。
②3个键盘事件:
□keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
□keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
□keyup:当用户释放键盘上的键时触发。
□与鼠标事件一样,支持相同的修改键。而且键盘事件对象中也有shifkey、ctrlkey、altkey、metakey属性。
4.3.1 键码
①发生keydown和keyup事件时,event对象的keycode属性会包含一个代码与键盘上一个特定的键对应。
②keycode属性值与ascii码中对应小写字母或数字的编码相同。查表p304
③dom和ie的event对象都支持keycode属性。
④以下是无论keydown或keyup事件都会存在的一些特殊情况:
□firefox和opera中,按分号键时keycode值为59,即ascii中值,但ie和safari返回186,即键盘键码
□safari3之前版本中,上下左右,上下翻页返回大于6300的值。
□在opera9.5之前版本中,会将非数字字母键的keycode设为ascii编码。
□在safari3之前版本,不会因为按下了制表、上档、控制或替代键而触发keydown和keyup事件。
4.3.2 字符编码
□firefox、chrome和safari的event对象支持一个charcode属性,这个属性只有在发生keypress时才有值,为字符的ascii编码。此时的keycode值有可能为0,也可能为所按键码。
□ie和opera则是在keycode中保存ascii编码。
□跨浏览器取字符编码
var eventutil = {
//省略的代码
getcharcode : function(event){
if(typeof event.charcode == number){
return event.charcode;
}else{
return event.keycode;
}
},
};
4.3.4 textinput事件
□当用户在可编辑区域中输入字符时,会触发textinput事件。
◇只有编辑区域才能触发textinput事件。
◇事件只会在用户按下能够输入实际字符的键时才会被触发。
◇事件event对象中包含一个data属性,保存用户输入的字符。
□2008年上半年,仅safari3和chrome支持。
4.3.4 设备中的键盘事件。(略)
4.4 html事件
①定义:html事件指的是那些不一定与用户操作有关的事件。
□load事件:
◇当页面完全加载后window上面触发。
◇当所有框架都加载完毕时在框架集上面触发
◇当图像加载完毕时在元素上面触发
◇当嵌入的内容加载完毕时在元素上面触发
□unload事件:
◇当页面完全卸载后在window上面触发
◇当所有框架都卸载后在框架集上面触发
◇嵌入的内容卸载完毕后在元素上面触发
□abort事件:在用户停止下载过程时,如果嵌入的内容没有加载完,则在元素上面触发。
□error事件:
◇当发生javascript错误时在window上面触发
◇当无法加载图像时在元素上面触发
◇当无法加载嵌入内容时在元素上面触发
◇当有一或多个框架无法加载时在框架集上触发
□select事件:当用户选择文本框(或)中的一个或多个字符时触发。
□change事件:当文本框(或)失去焦点或者取得焦点后其值被改变时触发。
□submit事件:当用户单击提交按钮时在元素上面触发。
□reset事件:当用户单击重置按钮时在元素上触发。
□resize事件:当窗口或框架的大小变化时在window或框架上触发。
□scroll事件:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。元素中包含所加载页面的滚动条。
□focus事件:当页面或元素获得焦点(用户单击、按制表键进入或以其他方式激活元素时)在window及相应元素上面触发。
□blur事件:当页面或元素失去焦点时在window及相应元素上面触发。
②多数html事件都与window对象或表单控件相关。要确定浏览器是否支持dom规定的html事件代码如下:
var issupport = document.implementation.hasfeature(htmlevents,2.0);
4.4.1 加载(load)事件
①有两种定义onload事件处理程序的方式
□eventutil.addhandler(window,load,function(event){alert(loaded);});
□为元素添加一个onload特性(不建议)
②图像上面也可以触发load事件,无论是dom中图像还是html中的图像元素。
□可以像使用元素一样使用image对象,不过无法添加到dom树中。
例子:使用image对象在客户端预加载图像
eventutil.addhandler(window,load,function(){
var image = new image();
eventutil.addhandler(image,load,function(event){
alert(image loaded!);
});
image.src = smile.gif;
});
③firefox、opera、chrome和safari3以上,
