javascript--13事件

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块封装,以便错误不会浮出水面。
<input type="button" value="click me" onclick="try{show();}catch(ex){}"/>
◇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上面触发。
◇当所有框架都加载完毕时在框架集上面触发
◇当图像加载完毕时在<img>元素上面触发
◇当嵌入的内容加载完毕时在<object>元素上面触发
□unload事件:
◇当页面完全卸载后在window上面触发
◇当所有框架都卸载后在框架集上面触发
◇嵌入的内容卸载完毕后在<object>元素上面触发
□abort事件:在用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上面触发。
□error事件:
◇当发生JavaScript错误时在window上面触发
◇当无法加载图像时在<img>元素上面触发
◇当无法加载嵌入内容时在<object>元素上面触发
◇当有一或多个框架无法加载时在框架集上触发
□select事件:当用户选择文本框(<input>或<textarea>)中的一个或多个字符时触发。
□change事件:当文本框(<input>或<textarea>)失去焦点或者取得焦点后其值被改变时触发。
□submit事件:当用户单击提交按钮时在<form>元素上面触发。
□reset事件:当用户单击重置按钮时在<form>元素上触发。
□resize事件:当窗口或框架的大小变化时在window或框架上触发。
□scroll事件:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加载页面的滚动条。
□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");});
□为<body>元素添加一个onload特性(不建议)
②图像上面也可以触发load事件,无论是DOM中图像还是HTML中的图像元素。
□可以像使用<img>元素一样使用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以上,<script>元素也会触发load事件。与图像不同,只有在设置了src并添加到文档中才会开始下载JS文件。(IE不支持)
④IE和Opera还支持<link>元素上load事件。与<script>节点类似,在未指定href属性并将<link>元素添加到文档之前也不会开始下载样式表。
4.4.2 卸载事件(unload)
□与load事件对应的是unload事件,这个事件在文档完成卸载后触发。
□利用这个事件最多的情况是消除引用,以免内存泄露。
□使用方式一:EventUtil.addHandler(window,"unload",function(event){alert("unload");});
□使用方式二:为<body>元素添加一个特性。
4.4.3 调整大小(resize)事件
①当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件。
②事件在window(窗口)上触发,因此可以通过JavaScript或<body>元素中的onresize特性来指定事件处理程序。
③IE、Safari、Chrome和Opera会再浏览器窗口变化了1像素时触发resize事件,然后随着变化不断重复触发。因此不要加入大量计算代码。
4.4.4 滚动(scroll)事件(略)
4.5 变动事件
①定义:DOM2级的变动(mutation)事件能在DOM中的某一部分发生变化是给出提示。
②DOM2级定义了如下变动事件:
□DOMSubtreeModified:在DOM结构中发生任何变化时触发。
□DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中时触发。
□DOMNodeRemoved:在节点从其父节点中移除时触发。
□DOMNodeInsertIntoDocument:在一个节点被直接插入文档或通过子树间接插入文档之后触发。在DOMNodeInsert之后触发。
□DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。这个事件在DOMNodeRemove之后触发。
□DOMAttriModified:在特性被修改之后触发。
□DOMCharacterDataModified:在文本节点的值发生变化时触发。
③检测浏览器是否支持变动事件:
Var isSupport = document.implementation.hasFeature("MutationEvents","2.0");
□实际上,即使返回true也不代表浏览器支持。P312
4.6 专有事件
4.6.1 上下文菜单(contextmenu事件)
①contextmenu事件:用以表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。
②contextmenu事件是冒泡的,因此可以为document指定一个事件处理程序,用以处理页面中发生的所有类似事件。
③事件的目标是发生用户操作的元素,在所有浏览器中都可以取消这个事件:兼容的DOM的:event.preventDefault(),在IE中,event.returnValue设为false。
④contextmenu事件属于鼠标事件,其对象中包含与光标位置有关的所有属性。
⑤通常使用contextmenu事件来显示自定义的上下文菜单,而使用onclick事件处理程序隐藏该菜单。
EventUtil.addHandler(window, "load", function(event){
var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "contextmenu", function(event){
event = EventUtil.getEvent(event);
EventUtil.preventDefault(event);
var menu = document.getElementById("myMenu");
menu.Style.left = event.clientX + "px";
menu.style.top = event.clientY + "px";
menu.style.visibility = "visible";
});
EventUtil.addHandler(document, "click", fucntion(event){
document.getElementById("myMenu").style.visibility = "hidden";
});
});
4.6.2 卸载前(beforeunload)事件
①beforeunload事件:会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面。
②IE和Firefox支持,会弹出询问用户是否离开对话框。Safari和Chrome支持,但不妨碍事件继续,没有显示对话框。Opera9.5之前不支持。
EventUtil.addHandler(window, "beforeunload", function(event){
event = EventUtil.getEvent(event);
event.returnValue = "I'm really miss you";
});
4.6.3 鼠标滚轮(mousewheel)和DOMMouseScroll事件
①在鼠标滚轮滚动时触发
②mousewheel事件为IE、Opera、Chrome、Safari支持,事件对应event中包含鼠标事件的所有标准信息外,还包含一个特殊的wheelEdlta属性。是120的倍数,向后滚-120倍数。
③9.5之前的版本中,wheelDelta值正负号颠倒。
④Firefox支持DOMMouseScroll事件,滚轮信息存在detail属性中,前滚为-3倍数,后滚为3倍数。
兼容代码:
var EventUtil = {
getWheelDlta : function(event){
if(event.wheelDelta){
Return (client.egine.opera && client.egine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta);
}else{
return -event.detail * 40;
}
},
};
4.6.4 DOMContentLoaded事件
①DOMContentLoaded事件在形成完整的DOM树之后就触发,不会理图像、JavaScript文件、CSS文件或其他资源是否已经下载完毕。
EventUtil.addHandler(document,"DOMContentLoaded", function(event){
alert("Content loaded");
});
②对于不支持DOMContentLoad的浏览器,建议在页面加载期间设置一时间为0毫秒的超时调用。
setTimeout(function(){
//在此添加事件处理程序
}, 0);
4.6.5 就绪状态变化(readystatechange)事件
①IE为DOM文档中某些部分提供了readystatechange事件:提供与文档或元素的加载状态有关的信息。
②支持readystatechange事件的每个对象都有一个readyState属性
□uninitialized(未初始化):对象存在但尚未初始化;
□loading(正在加载):对象正在加载数据;
□loaded(加载完毕):对象加载数据完成;
□interactive(交互):可以操作对象了,但还没完全加载
□complete(完成):对象已经加载完毕。
③在IE中使用readystatechange近似模拟DOMContentLoaded事件。
EventUtil.addHandler(document, "readystatechange", function(event){
if(document.readyState == "interactive" || document.readyState == "complete"){
eventUtil.removeHandler(document, "readystatechange", arguments.callee);
alert("Content load");
}
});
4.6.6 页面显示(pageshow)和页面隐藏(pagehide)事件
①Firefox和Opera有一个特性,名叫“往返缓存”(back-forward cache,或bfcache)。可以在用户使用浏览器的“后退”和“前景”按钮时加快页面的转换速度。
②pageshow事件:在页面显示时触发,无论页面是否来自bfcache。在重新加载的页面中,pageshow会在load事件触发后触发,而对于bfcache中的页面,pageshow会在页面状态完全恢复的那一刻触发。
□pageshow事件的event对象包含一个名为persisted的布尔值属性。如果页面保存在bfcache中,则只为true,反之则反。
③pagehide事件:事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发。
□第一次触发pagehide时,persisted就会变成true(除非页面不存在bfcache中)
④指定了onunload事件处理程序的页面自动排除在bfcache之外。
4.6.7 移动Safari支持的事件
①方向变化(ovientationchange)事件
②触摸事件
③手势事件
5.内存和性能
①在JavaScript中,添加到页面上的事件处理程序数量直接关系到页面整体运行性能。
□每个函数是对象,都会占用内存;内存中的对象越多,性能越差。
□必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
5.1 事件委托
①事件委托:解决“事件处理程序过多”问题。
原理:利用事件冒泡,只指定一个事件处理程序,来管理某一类型的所有事件。
var list = document.getElementById("myLinks");
EventUtil.addHandler(list,"click",function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch(target.id){
case "doSomething" : document.title = "I changed the title";
break;
case "goSomewhere" : location.href = "http://www.wrox.com";
break;
case "sayHi" : alert("hi");
break;
}
},false);
②如果可行的话,也可以考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型事件。
优点:
□document对象很快就可以访问,而且可以在页面生命周期的任何时间点上为它添加事件处理程序(无需等待DOMContentLoad或Load事件)。即只要可单击的元素呈现在页面上,就可以立即具备适当的功能。
□在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少。
□整个页面占用的内存空间更少,能提升整体性能。
③使用范围
□适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress。
□虽然mouseover和mouseout事件也冒泡,但要适当处理他们并不容易,而且经常需要计算元素的位置(因当鼠标从一个元素移动到其子节点时或者鼠标移出该元素时,就会触发mouseout事件)
5.2 移除事件处理程序
①浏览器页面代码与JavaScript代码之间建立的链接越多,执行越慢。可用事件委托技术,限制建立的链接数量。在不需要的时候移除事件处理程序。
②内存中留有过时不用的“空事件处理程序”(dangling event handler)也是造成web应用程序内存与性能问题的主要原因。
1)第一种情况:从文档中移除带有事件处理程序的元素时。如removeChild()和replaceChild()方法,更多是发生在使用innerHTML替换页面中某一部分的时候。
□手动解除这些“空事件处理程序”,释放内存。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
//先执行某些操作
btn.onclick = null; //移除事件处理程序
document.getElementById("myDiv").innerHTML = "prossing";
};
2)另一种情况:卸载页面的时候。若没有清理干净会滞留内存。
□在卸载之前,先通过onunload事件处理程序移除所有事件处理程序。
6.模拟事件
可以使用JavaScript在任意时刻来触发特定的事件,而此时的事件就如同浏览器创建的事件一样。即该冒泡会冒泡,且照样导致浏览器执行已经制定处理它们的事件处理程序。
6.1 DOM中的事件模拟
■可以在docuemnt对象上使用createEvent()方法创建对象。这个方法接受一个参数,即表示创建事件类型的字符串,如下:
□UIEvents:一般化的UI事件。鼠标事件和键盘事件都继承自UI事件。
□MouseEvents:一般化的鼠标事件。
□MutationEvents:一般化的DOM变动事件。
□HTMLEvents:一般化的HTML事件。
■各种类型创建的event对象,都有一个特殊的方法,为它传入适当的数据可以初始化该event对象。
■触发事件:调用dispatchEvent()方法,传入要触发事件的event对象。
6.1.1 模拟鼠标事件
①创建新的鼠标事件对象并为其指定必要的信息,就可以模拟鼠标事件。创建鼠标事件的对象方法是createEvent(),传入字符串"MouseEvents"。返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息。
initMouseEvent()方法15个参数:
□type(字符串):表示要触发的事件类型名,例如“click”。
□bubbles(布尔值):表示事件是否应该冒泡。
□cancelable(布尔值):表示事件是否可以取消。
□view(AbstractView):与事件关联的视图。几乎总设为document.defaultView
□detail(整数):与事件有关的详细信息。通常设为0
□screenX(整数):事件相对于屏幕的X坐标。
□screenY
□clientX(整数):事件相对于视口的X坐标。
□clientY
□ctrlKey(布尔值):表示是否按下了Ctrl键。默认为false
□altKeyu
□shiftKey
□metaKey
□button(整数):表示按下了哪一个鼠标键。默认0
□relatedTarget(对象):表示与事件相关的对象。这参数只在模拟mouseover或mouseout时使用。
var btn = document.getElementById("myBtn");
//创建事件对象
var event = document.creeateEvent("MouseEvents");
//初始化事件对象
event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
//触发事件
btn.dispatchEvent(event);
6.1.2 模拟键盘事件
①Firefox中调用createEvent()并传入"keyEvents"就可以创建一个键盘事件。返回的事件对象会包含一个initKeyEvent()方法。
②在其他浏览器中,则需要创建一个通用的事件,然后再向事件对象添加键盘事件特有的信息。
var textbox = document.getElementById("myTextbox");
//创建事件对象
var event = document.createEvent("Events");
//初始化事件对象
event.initEvent(type,bubbles,cancelable);
event.view = document.defaultView;
event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.metaKey = false;
event.keyCode = false;
event.charCode = false;
//触发事件
textbox.dispatchEvent(event);
6.1.3 模拟其他事件(变动事件和HTML事件)
6.2 IE中的事件模拟
IE中思路与DOM相同,其中的区别:
□IE使用document.createEventObject()方法创建对象。此法不接受参数,仅返回一个通用event对象。必须手工为此对象添加属性。
□触发事件在目标上调用fireEvent()方法。两个参数:事件处理程序名和event对象。在调用fireEvent()方法时,会自动为event对象添加srcElement和type属性。
第十三章 表单脚本
1.表单
①JavaScript中表单<form>对应HTMLFormElement类型。HTMLFormElement继承了HTMLElement,因而与其它HTML元素具有相同的默认属性。也有其独有属性和方法:
□acceptCharset:服务器能够处理的字符集;等价HTML中的accept-charset特性。
□action:接受请求的URL;等于HTML中的action特性。
□elements:表单中所有控件的集合(HTMLCollection)。
□enctype:请求的编码类型;等价于HTML中的enctype特性。
□length:表单中控件的数量。
□method:要发送的HTTP请求类型,通常是“get”或“post”;等价于HTML的method特性。
□name:表单名称;等价于HTML的name特性。
□reset():将所有表单域重置为默认值。
□submit():提交表单。
□target:用于发送请求和接收响应的窗口名称;等价于HTML的target特性。
②获取表单
var form = document.getElementById("form1");
var firstForm = document.forms[0];
var myForm = document.forms["form2"];
1.1 提交表单
①提交表单的三种方法:
□通用按钮:<input type="submit" value="submit Form"/>
□自定义按钮:<button type="submit">Submit Form</button>
□图像按钮:<input type="image" src="graphic.gif"/>
②在提交表单时,浏览器会将请求发送给服务器之前触发submit事件。这样就有机会验证表单数据,并据以决定是否允许表单提交。
var form = document.getElementById("myForm");
EventUtil.addHandler(form,"submit",function(event){
//取得事件对象
event = EventUtil.getEvent(event);
//阻止默认事件
EventUtil.preventDefault(event);
});
③提交表单的最大问题是重复提交表单。解决办法:在第一次提交表单后就禁用提交按钮。
④在JavaScript中,可以以编程方式提交表单。
var form = document.getElementById("myForm");
//提交表单
form.submit(); //此方法提交表单不会触发submit事件
1.2 重置表单
①重置表单方式
□通用按钮:<input type="reset" value="Reset Form"/>
□自定义按钮:<button type="reset">Rest Form</button>
②用户单击重置按钮重置表单是,会触发reset事件,利用这个机会可在必要时取消重置操作。
③可用JavaScript重置表单:form.reset();与调用submit()方法不同,调用reset()方法会想单击重置按钮一样触发reset事件。
1.3表单字段
①访问表单字段方法:
□使用原生DOM方法。
□通过表单的elements属性,该属性是表单中所有元素的集合。
◇表单字段按标记顺序保存在elements中。
◇可以按照位置和name特性访问它们。
◇如果多个表单控件都使用一个name特性,则返回一个NodeList。
var form = document.getElementById("form1");
//取得表单中的第一个字段
var field1 = form.elements[0];
//取得名为textbox1的字段
var field2 = form.elements["textbox1"];
//取得表单中包含的字段的数量
var fieldCount = form.elements.length;
1.3.1 共有的表单字段属性
①表单字段共有的属性和方法如下:
□disabled:布尔值,表示当前字段是否被禁用。
□form:指向当前字段所属表单的指针,只读。
□name:当前字段的名称。
□readOnly:布尔值,表示当前字段是否只读。
□tabIndex:表示当前字段的切换(tab)序号。
□type:当前字段的类型,如:“checkbox”、“radio”等等。
□value:当前字段将被提交给服务器的值。
②不能用click事件处理submit事件,因不知道在submit前触发click还是submit后触发click。
1.3.2 共有的表单字段方法
□focus()方法:将浏览器的焦点设置到表单字段。
□blur()方法:从元素中移走焦点。
1.3.3 共有的表单字段事件
除了支持鼠标事件、键盘、变动和HTML事件外,所以表单字段支持下列3个事件:
□blur:当前字段失去焦点时触发。
□change:对于<input>和<textarea>元素,他们失去焦点切value值改变时触发;对于<select>元素,在其选项改变时触发。
□focus:当前字段获得焦点时触发。
2.文本框脚本
①在HTML中两种方法表现文本框
□使用<input>元素的单行文本框
◇显示25个字符,输入不超过50个字符。size="25" maxlength="50"
□<textarea>元素,多行文本框
◇25行,5列。rows="25" cols="5"
②设置文本框的值
□使用value属性读取或设置文本框的值,不建议使用DOM方法。
□使用DOM方法:setAttribute()设置value特性或修改<textarea>元素第一个子节点这样对value值的修改不一定反映到DOM中。
2.1 选择文本
①<input>单行文本和<textarea>多行文本都支持select()方法,这个方法用于选择文本框中所有文本。
EventUtil.addHandler(textbox, "focus", function(){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
target.select();
});
2.1.1 选择(select)事件
select事件触发的情况复杂,要想编写跨浏览器代码,必须手工取得对事件目标的引用:
var textbox = document.forms[0].elements["textbox1"];
EventUtil.addHandler(textbox,"select",function(event){
var target = document.forms[0].elements["textbox1"];
alert("text selected");
});
2.1.2 取得选择的文本
□文本框的两个属性:selectionStart和selectionEnd。基于0的数值,表示所选文本范围。FF、Safari、Chrome、Opera支持。
□IE有一个document.select对象,其中保存用户整个文档范围内选择的文本信息。与select事件一起,可假定为文本内容。取值前须创建一个范围。
function getSelectedText(textbox){
if(document.selection){
return document.selection.createRange().text;
}else{
return textbox.value.substring(textbox.selectionStart,textbox.selectionEnd);
}
}
2.1.3 选择部分文本
□setSelectionRange()方法。接受两个参数:要选择的第一个字符的索引和要选择的最后一个字符之后的字符索引。FF、Chrome、Safari、Opera支持。
□IE为文本框提供了createTextRange()方法,要选择文本框中的部分文本,必须首先使用这个方法创建一个范围,并将其放在恰当位置上。再使用moveStart()和moveEnd()这两个方法将范围移动位置。
□兼容代码
function selectText(textbox,strtIndex,stopIndex){
if(textbox.setSelectionRange){
textbox.setSelectionRange(startIndex,stopIndex);
}else if(textbox.createTextRange){
var range = textbox.createTextRange();
range.collapse(true);
range.moveStart("character",startIndex);
range.moveEnd("charater",stopIndex-startIndex);
range.select();
}
Textbox.focus();
}
2.2.1 过滤输入
例为屏蔽非数字值,而不屏蔽ctrl组合键
EventUtil.addHandler(textbox,"keypress",function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharcode(event);
if(!/\d/.test(String.fromCharCode(charCode))&&charCode>9&&!event.ctrlkey){
EventUtil.preventDefault(event);
}
});
2.2.2 操作剪贴板
①6个剪贴板事件:
□beforecopy:在发生复制操作前触发;
□copy:在发生复制操作时触发
□beforecut:在发生剪切操作时触发。
□cut:在发生剪切操作时触发
□beforepaste:在发生粘贴操作前触发
□paste:在发生粘贴操作时触发
②访问剪贴板中数据,可使用clipboaredData对象,有3个方法:
getDate()、setDate()和clearData()
兼容方法:
var EventUtil = {
getClipboardText : function(event){
var clipboardData = (event.clipboardData || window.clipboardData);
return clipboardData.getData("text");
},
setClipboardText : function(event, value){
if(event.clipboardData){
return event.clipboardData.setData("text/pain",value);
}else if(window.clipboardData){
return window.clipboardData.setData("text",value);
}
},
};
3.选择框脚本
①选择框是通过<select>和<option>元素创建的。HTMLSelectElement类型还提供了下列属性和方法:
□add(newOption, relOption):向空间按中插入新元素,其位置在指定项relOption之前
□multiple:布尔值,表示是否允许多项选择;等价于HTML中的multiple特性。
□options:控件中所有<option>元素的HTMLCOllection。
□remove(index):移除给定位置选项。
□selectedIndex:基于0的选中项的索引,如果没有选中项,则值为-1.对于支持多选的控件,只保存选项中第一项的索引。
□size:选择框中可见的行数;等价于HTML中的size特性
□type:"select-one"或"select-multiple",取决于HTML代码中有没multiple特性。
□value:取决于当前选中项。
②DOM中,每个<option>元素都有一个HTMLOptionElement对象表示。其对象添加以下属性:
□index:当前选项在options集合中的索引
□label:当前选项的标签;等价于HTML中的label特性
□selected:布尔值,表示当前选项是否被选中。设true可选中。
□text:选项的文本
□value:选项的值(等价于HTML中的value特性)
3.1 选择选项
□对于只允许一项的选择框,使用选择框的selectedIndex属性。
var selectedOption = selectbox.options[selectbox.selectedIndex];
□对于多选的选择框,遍历检查其selected属性。
3.2 添加选项
①DOM方法:
var newOption = document.createElement("option");
newOption.appendChild(document.createTextNode("Option text");
newOption.setAttribute("value","Option value");
selectbox.appendChild(newOption);
②Option构造函数,接受两个参数:文本(text)和值(value);第二个可选。(IE不可用)
Var newOption = new Option("Option text","Option value");
Selectbox.appendChild(newOption); //在IE中有问题
③使用选择框的add()方法,接受两个参数:要添加的新选项和将位于新选项之后的选项。
Var newOption = new Option("Option text", "Option value");
Selectbox.add(newOption, undefined); //最佳方案
3.3 移除选项
①可使用DOM的removeChild()方法,为其传入要移除的项。
selectbox.removeChild(selectbox.option[0]);
②使用选择框的remove()方法。接受一个参数,即要移除项的索引。
selectbox.remove(0); //移除第一个选项
③将相应项设为null
3.4 移动和重排选项
①使用appendChild()方法,讲一个选择框中选项移到另一个选择框。
②使用insertBefore()实现重排
4.表单序列化
①浏览器发数据至服务器方式:
□对表单字段的名称和值进行URL编码,使用和号(&)分隔。
□不发送禁用的表单字段
□只发送勾选的复选框和单选按钮
□不发送type为“reset”和“button”的按钮。
□多选选择框中的每个选中值单独一个条目。
□在单击提交按钮表单的情况下,也会发送提交按钮;否则,不发送提交按钮。也包括type为"image"的<input>元素。
□<select>元素的值,就是选中<option>元素的value特性的值。如果<option>元素没有value特性,则是<option> 元素的文本值。
②表单序列化代码:
function serialize(form){
var parts = new Array();
var field = null;
for(var i = 0, len = form.elements.length; i < len; i++){
field = form.elements[i];
switch(field.type){
case "select-one" :
case "select-multiple" :
for(var j=0, optLen = field.options.length; j<optLen; j++){
var option = field.options[j];
if(option.selected){
var optValue=" ";
if(potion.hasAttribue){
optValue = (option.hasAttribute("value") ? Option.value : option.text);
}else{
optValue = (option.attributes["value"].specified ? Option.value : option.text);
}
parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(optValue));
}
case undefined : //字段集
case "file" : //文件输入
case "submit" : //提交按钮
case "reset" : //自定义按钮
Break;
case "radio" : //单选按钮
case "checkbox" : //复选框
If(!field.checked){break;}
default :
parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.name));
return parts.join("&");
}
}
}
5.富文本编辑(略)
1、事件冒泡

要理解事件冒泡,就得先知道事件流。事件流描述的是从页面接收事件的顺序,比如如下的代码:

XHTML

1
2
3
4
5
<body>
  <div>
    click me!
  </div>
</body>
如果在body和div内都注册了click的事件监听,之后又点击了div区域,是body先响应还是div先响应?有意思的是,当时的浏览器开发团队IE和Netscape提出了差不多完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape提出的事件流是事件捕获流。

IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。如上代码,点击click事件会这样传播:div->body->html->document(虽然我没写html元素,但是页面上默认还是会存在的)

现代的所有浏览器都支持事件冒泡,但还是有些细微差别。IE5.5以及更早版本中的事件冒泡会跳过<html>元素(从body直接跳到document)。IE9、ff、chrome和safari则将事件一直冒泡到window对象。

2、事件捕获

Netscape团队则提出另一种事件流-事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件,如果仍以上面的代码举例:document->html->body->div。

虽然事件捕获是Netscape唯一支持的事件流模型,但是IE9、Safari、chrome、opera和ff目前也都支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件的。

因为老版本的浏览器不支持事件捕获,所以我们建议使用事件冒泡。

3、DOM事件流

“DOM2级事件”规定事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。还是上面的代码作为例子,单击div元素会按照如下顺序触发事件:document->html->body->div->body->html->document。

在DOM事件流中,实际的目标(div)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件到body就停止了,下一个阶段是“处于目标”阶段,于是事件在div上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回文档。但是多数支持DOM事件流的浏览器都实现了一种特定的行为:即使“DOM2级事件”规范明确要求捕获阶段不会涉及目标事件,但IE9、safari、chrome、ff和opera9.5及更高版本都会在捕获阶段触发事件对象上的事件,结果就是有两个机会在目标对象上面操作。(IE9、opera、ff、chrome和Safari都支持DOM事件流,IE8及更早版本不支持DOM事件流)。

4、事件处理程序

响应某个事件的函数就叫做事件处理程序。

DOM0级的事件处理程序很简单,onclick就是常用的DOM0级事件处理函数,只会在冒泡阶段被处理。

而“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener(),所有DOM节点都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用。DOM2级方法添加事件处理程序的好处是可以添加多个事件处理程序,会按照添加顺序被处理(无论是捕获还是冒泡)。这也是为什么DOM0级事件兼容各种浏览器,我们却还是要使用DOM2的原因之一。


var div = document.getElementById('myDiv');
div.addEventListener('click', function() {
  console.log(this.id);
}, true);
 
div.addEventListener('click', function() {
  console.log('hello world');
}, true);
而IE与DOM不同,它有自己的方法:attachEvent()和detachEvent(),这两个方法接受相同的两个参数:事件处理程序名称和事件处理程序函数。由于IE8以及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段(所以不需要第三个参数)。


var div = document.getElementById('myDiv');
div.attachEvent('onclick', function() {
  console.log('hello world');
});
注意第一个参数是onclick,而非DOM标准的click。在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域,在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行,而在使用attachEvent()方法的情况下,事件处理程序在全局作用域中运行,因此this等于window(这点要特别注意!!!)。attachEvent()也能添加多个事件处理程序,但是事件的执行顺序和添加顺序相反。

5、跨浏览器的事件处理程序

因为浏览器之间的差异(其实就是IE大家都懂的),所以需要编写跨浏览器的事件处理程序。


var EventUtil = {
  addHandler: function(element, type, handler) {
    if (element.addEventListener) {  // DOM2
      element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {  // IE
      element.attachEvent('on' + type, handler);
    } else {  // DOM0
      element['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;
    }
  }
};
6、事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件有关的信息。坑爹的是DOM中的事件对象和IE又有不同的玩法。

先来说说DOM中的:


var div = document.getElementById('myDiv');
div.onclick = function(e) {
  console.log(e.type);
};
 
div.addEventListener('click', function(e) {
  console.log(e.type);
}, false);
上面代码我们应该都不陌生,分别实现了DOM0级和DOM2级的事件对象。

e有很多的属性和方法,这里提几个常用的。target和currentTarget,target指的是事件的真正目标,而currentTarget指的是当前的目标,正是利用target我们可以做事件代理。

要阻止特定事件的默认行为,我们可以使用preventDefault()方法,例如链接的默认行为就是在被单击时会导航到其href指定的url,如果你想阻止这个默认行为,那么通过链接的onclick事件处理程序可以取消它:


var link = document.getElementById('myLink');
link.onclick = function(e) {
  e.preventDefault();
};
只有cancelable属性设置为true的事件,才可以使用preventDefault()来取消其默认行为。

另外,stopPropagation()方法用于立即停止事件在DOM层中的传播,即取消进一步的事件捕获或冒泡。


var div = document.getElementById('myDiv');
div.onclick = function(e) {
  console.log('click!');
  e.stopPropagation();
};
 
document.body.onclick = function(e) {
  console.log('hello world');
};
而IE中的事件对象是这么用的:


var div = document.getElementById('myDiv');
div.onclick = function() {
  var e = window.e;
  console.log(e.type);
};
 
div.attachEvent('onclick', function(e) {
  // 也可以通过window.e访问
  console.log(e.type);
});
IE中的event对象也有很多属性和方法,比如srcElement就是和DOM中的target属性相同,而returnValue属性相当于DOM中的preventDefault()方法,它们的作用都是取消给定事件的默认行为。只要将该值设置为false,就可以阻止默认行为。相应地,canceBubble属性和DOM中的stopPropagation()方法作用相同,因为IE只支持冒泡,所以它只能取消事件冒泡。

跨浏览器的事件对象:

JavaS
var EventUtil = {
  getEvent: function(e) {
    return e ? e : window.e;
  },
 
  getTarget: function(e) {
    return e.target || e.srcElement;
  },
 
  preventDefault: function(e) {
    if (e.preventDefault) {
      e.preventDefault();
    } else {
      e.returnValue = false;
    }
  },
 
  stopPropagation: function(e) {
    if (e.stopPropagation) {
      e.stopPropagation()
    } else {
      e.cancelBubble = true;
    }
  }
}
7、事件委托

有了以上作为基础,事件委托应该是很简单了。什么是事件委托?对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click事件会冒泡到document层次,也就是说,我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。

举个经常举的例子,比如有如下代码:

XHTM
<ul id='myLink'>
  <li id='a'> apple </li>
  <li id='b'> banana </li>
  <li id='c'> orange </li>
</ul>
需要的效果是每点击相应的<li>选项,alert它里面的单词,或许很简单:

JavaScript
var lis = document.getElementsByTagName('li');
for(var i = 0, len = lis.length; i < len; i++) {
  lis[i].onclick = function() {
    alert(this.innerHTML);
  };
}
但是如上代码绑定了三个事件,我们知道每个事件绑定都需要占用一定的内存,更糟糕的是,如果在代码执行过程中,动态地又添加了一个li,这时它没有绑定click的事件,我们还需要手动添加!这时,我们就可以用到事件委托技术:

JavaScript
var f = document.getElementById('myLink');
f.onclick = function(e) {
  console.log(e.target.innerHTML);
};




<input type="button" value="Click Me" onclick"showMessage()"/>

通过HTML指定事件处理程序的的缺点是HTML与JavaScript代码紧密耦合。如果要更换事件处理程序,就要改动两个地方:HTML代码和JavaScript代码。

DOM0级事件处理程序

var btn=document.getElementById("myBtn");
btn.onclick=function(){
alert("Clicked");
}
在此,我们通过文档对象取得了一个按键的引用,然后为它指定了OnClink事件处理程序。但要注意,在这些代码运行以前不会指定事件处理程序,因此如果这些代码在页面中位于
按键后面,就有可能在一段时间内怎么单击都没有反应。
使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行;换句话说,程序中的this引用当前元素。来看一个例子:

var btn=document.getElementById("myBtn");
btn.onclick=function(){
alert(this.id); //"myBtn"
}
也可以删除DOM0级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为Null即可:

btn.onclick=null;
DOM2级事件处理程序

"DOM2级事件"定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener();
使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。


var btn=document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);

btn.addEventListener("click",function(){
alert("hello world!");
},false);

这里为按钮添加了两个事件处理程序。这两个事件处理程序会按照添加它们的顺序触发。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()
添加的匿名函数无法移除,如下面所示:


var btn=document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);

btn.removeEventListener("click",function(){ //无效
alert(this.id);
},false);

有效的:


var btn=document.getElementById("myBtn");
var handler=function(){
alert(this.id);
}
btn.addEventListener("click",handler,false);

btn.removeEventListener("click",handler,false); //有效

IE事件处理程序

IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。

事件对象

兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序使用什么方法(DOM0级或DOM2级),都会传入event对象。来看下面的例子:


var btn=document.getElementById("myBtn");

btn.onclick=function(event){
alert(event.type); //"click"
};

btn.addEventListener("click",function(event){
alert(event.type); //"click"
},false);

在需要通过一个函数处理多个事件时,可以使用type属性,例如:


var btn=document.getElementById("myBtn");
var handler=function(event){
switch(event.type){
case "click":
alert("Clicked");break;
case "mouseover":
alert("mouseover");break;
case "mouseout":
alert("mouseout");break;
}
}
btn.onclick=handler;
btn.onmouseover=handler;
btn.onmouseout=handler;

要阻止特定事件的默认行为,可以使用preventDefault()方法。

var link=document.getElementById("myLink");
link.onclick=function(event){
event.preventDefault();
}

只有cancelable属性设置为true的事件,才可以使用preventDefault()来取消默认行为。

事件类型

只要用户从一个页面切换到另一个页面,就会发生unload事件 。
只有在同一个元素上相继触发mousedown事件和mouseup事件,才会触发click事件。
DOM的button属性可能有如下3个值:0表示主鼠标按钮,1表示中间的鼠标按钮(滚轮),2表示次鼠标按钮
keydown:当用户按下键盘上的做任意键时触发,而且如果按住不放的话,会重复触发些事件。

事件委托

对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click事件会一直冒泡到
document层次。也就是说,我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。以下面的代码为例:

<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say Hi </li>
</ul>

var list=document.getElementById("myLinks");

EventUtil.addHandler(list,"click",function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);

switch(target.id){
case "doSomething":
document.tile="i changed the document's title";break;
case "goSomewhere":
location.href="http://www.wrox.com";break;
case "sayHi":
alert("hi");break;
}
}

最适合采用事件委托技术的事件包括click,mousedown,mouseup,keydown,keyup,keypress.

JavaScript和HTML之间的交互是通过用户和浏览器操作页面时引发的事件(event)来处理的。

事件流:意味着在页面上可有不仅一个,甚至多个元素响应同一事件。点击页面上面的按钮,会发生什么?实际上,是点击了按钮、它的容器及整个页面。从逻辑上说,每一个元素都按照特定的顺序响应那个事件。事件发生的顺序(也就是事件流)是IE4.0和Netscapse4.0在事件支持上的主要差别。

冒泡型事件:
IE上的解决方案是绰号为冒泡(dubbed bubbling)的技术。冒泡型事件的基本思想是,事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。
IE6下的冒泡过程如下图:

Mozilla 1.0下的冒泡过程如下图:


捕获型事件:
Netscape Navigator4.0使用了捕获型事件(event capturing)。事件的捕获和冒泡刚好是相反的两种过程。捕获型事件中,事件从最不精确的对象(document对象)开始触发,然后到最精确的对象。

DOM流事件:
DOM同时支持两种事件模型:捕获型事件和冒泡型事件,但是,捕获型事件先发生。两种事件都会触及DOM中的所有对象。

事件处理函数/监听函数:用于响应某个事件而调用的函数称为事件处理函数(event handler)或者DOM称之为事件监听函数(event listener)。事件处理函数有两种分配方式:在JavaScript中通过对象属性的方式设置或者在HTML中通过元素属性的方式设置。

为每个可用事件分配多个函数:
IE:在IE中,每个元素和window对象都有两个方法:attachEvent()和detachEvent()。attachEvent用来给一个事件附加事件处理函数,而detachEvent用来将事件处理函数分离出来。事件处理函数总是按照添加它们的顺序进行调用的(先添加、先调用)。
DOM:DOM方法addEventListener()和removeEventListener()用来分配和移除事件处理函数。与IE不同,这些方法需要三个参数:事件名称,要分配的函数和处理函数是用于冒泡阶段还是捕获阶段的标识。如果事件处理函数是用于捕获阶段,第三个参数为true,用户冒泡阶段,则为false。

事件对象:
基于不同浏览器开发的开发人员都知道,获取事件信息是很重要的。事件发生时,会创建包含关于刚刚发生的事件的信息的事件对象,包含的信息有:引起事件的对象,事件发生时的鼠标信息,事件发生时的键盘信息。
事件对象只在发生事件时才被创建,且只有事件处理函数才能访问。所有事件处理函数执行完毕后,事件对象就被销毁。

定位:
在IE中,事件对象是window对象的一个属性event。在事件中,按照var oEvent = window.event;来访问。尽管它是window的属性,event对象还是只能在事件发生时访问。所有的事件处理函数执行完毕后,事件对象就销毁。
DOM标准则说,event对象必须作为唯一的参数传递给事件处理函数。所以,在DOM兼容的浏览器(如Mozilla、Safari和Opera)中访问事件对象,要用:var oEvent = arguments[0];或者直接作为参数。oDiv.onclick = function (oEvent) {};

事件的属性和方法:
IE和DOM标准的相似点:
获取事件类型var sType = oEvent.type;
获取按键代码(keyDown/keyUp事件)var iKeyCode = oEvent.keyCode;
检测Shift、Alt、Ctrl键var bShift = oEvent.shiftKey; altKey ctrlKey
获取客户端坐标var iClientX = oEvent.clientX; client
获取屏幕坐标 var iScreenX = oEvent.screenX; screen

IE和DOM标准的不同点:
获取目标IE:oEvent.srcElement   DOM:oEvent.target
获取字符代码
组织某个事件的默认行为IE:oEvent.returnValue = false;   DOM:oEvent.preventDefault();
停止事件复制(冒泡) IE:oEvent.cancelBubble = true;  DOM:oEvent.stopPropagation();


事件的类型:鼠标事件、键盘事件、HTML事件、变化事件(DOM标准事件)
鼠标点击事件的触发顺序:mousedown、mouseup、click、mousedown、mouseup、click、dblclick
鼠标移动事件的触发顺序:mouseout、mousemove、moseover

IE中是冒泡型事件,即从最特定的事件目标到最不特定的事件目标
一、事件流

  IE中是冒泡型事件,即从最特定的事件目标到最不特定的事件目标。
  Netscape Navigator使用的是捕获型事件,这个跟IE中采用的冒泡型事件相反。
  DOM事件流同时支持两种事件模型,但捕获型事件先发生。

二、事件处理函数/监听函数

  事件是用户或浏览器自身进行的特定行为。这些事件都有自己的名字,如click、load、mouseover等。
  事件处理函数有两种分配方式:在JavaScript中或者在HTML中。
  如果在JavaScript中分配事件处理函数,则首先要获得要处理的对象的引用,然后将函数赋值给对应的事件处理函数属性,像这样(事件处理函数名称必须小写):

var oDiv=document.getElementById("div1");
oDiv.onclick=function(){
alert("I was clicked");
}

  如果在HTML中分配事件处理函数,则只要在HTML标签中添加事件处理函数的特征,并在其中包含合适的脚本作为特性值就可以了,如下:

<div onclick="alert('I was clicked')"></div>  

为了给每个可用事件分配多个事件处理函数,IE和DOM各提供了自己的方法。
  IE中每个元素和window对象都有两个方法:attachEvent()和detachEvent(),顾名思义,前者用来给一个事件附加事件处理函数,后者用来将事件处理函数分离出来。每个方法都有两个参数:要分配的事件处理函数名字及一个函数。如:

var fnClick=function(){
alert("Clicked");
}
var fnClick2=function(){
alert("Click2");
}
var oDiv=document.getElementById("div");
oDiv.attachEvent("onclick",fnClick);
oDiv.attachEvent("onclick",fnClick2)
oDiv.detachEvent("onclick",fnClick);
oDiv.detachEvent("onclick",fnClick2);

DOM中采用了addEventListener()和removeEventListener()来分配和移除事件处理函数。与IE不同的是这些方法有三个参数,第三个参数标识是用于冒泡阶段还是捕获阶段。用于捕获阶段为true,用于冒泡阶段则为false。移除时第三个参数要跟添加时保持一致。如:

var fnClick=function(){
alert("Clicked");
}
var fnClick2=function(){
alert("Click2");
}
var oDiv=document.getElementById("div");
oDiv.addEventListener("onclick",fnClick,false);
oDiv.addEventListener("onclick",fnClick2,false)
oDiv.removeEventListener("onclick",fnClick,false);
oDiv.removeEventListener("onclick",fnClick2,false);


三、事件对象

  事件对象一般包含的信息是:引起事件的对象,事件发生时鼠标的信息,事件发生时键盘的信息。
定位
  IE中事件对象是window对象的一个属性event。事件处理函数必须这样访问事件对象:

oDiv.onclick = function(){
var oEvent=window.event;
}

DOM标准则说,event对象必须作为唯一的参数传递给事件处理函数。所以,在DOM兼容的浏览器(如Mozilla、Safair、Opera)中访问事件对象为:

oDiv.onclick=function(){
var oEvent=arguments[0];
}
//or
oDiv.onclick=function(oEvent){
}

属性方法相似性

  1、获取事件类型:oEvent.type
  2、获取按键代码:oEvent.keyCode
  3、检测Shift、Alt、Ctrl键:oEvent.shiftKey;oEvent.altKey;oEvent.ctrlKey;
  4、获取客户端鼠标坐标:oEvent.clientX;oEvent.clientY;
  5、获取屏幕坐标:oEvent.screenX;oEvent.screenY;

属性方法区别

  1、获取目标:IE用srcElement,DOM用target;
  2、获取按键字符代码:IE用keyCode,DOM用charCode和String.fromCharCode;
  3、阻止某个事件的默认行为:IE用oEvent.returnValue=false,DOM用preventDefault()方法;
  4、停止事件冒泡:IE中用oEvent.cancelBubble=true;DOM中用oEvent.stopPropagation();

四、事件的类型

  1、鼠标事件
  鼠标事件包含click、dblclick、mousedown、mouseout、mouseover、mouseup、mousemove。
  事件顺序:dblclick事件会先后触发以下事件:mousedown、mouseup、click、mousedown、mouseup、click、dblclick。
  2、键盘事件
  键盘事件包括:keydown、keypress、keyup。
  事件顺序:用户按一次某字符按键时,会先后触发以下事件:keydown、keypress、keyup。如果按一次某非字符按键时,会先后触发以下事件:keydown、keyup。
  3、HTML事件
  HTML事件包括:load、unload、abort、error、select、change、submit、reset、resize、scroll、focus、blur。
  4、变化事件
  虽然变化事件已经是DOM标准的一部分,但是目前还没有任何主流的浏览器实现了它。因此这里只是列举出来。
  变化事件包括:DOMSubtreeModified、DOMNodeInserted、DOMNodeRemoved、DOMNodeRemovedFromDocument、DOMNodeInsteredIntoDocument。


1. 事件流
事件流描述的是从页面中接收事件的顺序.IE的事件流为事件冒泡流,而网景浏览器为事件捕获流.
1. 事件冒泡和事件捕获.
事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档).例如:
<html>
<head>
    <title>Event Bubbling Example</title>
</head>
<body>
    <div id="myDiv">Click Me</div>
</body>
</html>
则click事件的传播顺序如下:

而事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件,其传播顺序如下:

2. DOM事件流
DOM2级事件规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段.事件捕获为捕获事件提供了机会,然后是实际的目标接收到事件,最后的冒泡阶段对事件作出响应.

2. 事件处理程序
事件就是用户或浏览器自身执行的某种动作,响应某个事件的函数为事件处理程序,以"on"开头.
1. HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定.这个特性的值应该是能够执行的javascript代码.例如:
<input type="button" value="Click Me" onClick="document.write('hello world')"/>
但是在HTML中定义的事件处理程序可以包含要执行的具体动作(有权访问全局作用域中的任何代码):
<html>
<script type="text/javascript">
    function showMessage() {
        document.write("Hello world!");
    }
</script>
<body>
    <input type="button" value="Click Me" onClick="showMessage()"/>
</body>
</html>
这样指定事件处理程序具有一些独到之处.因为这样会创建一个封装着元素属性值的函数,而这个函数有一个局部变量event,也就是事件对象:
<input type="button" value="Click Me" onClick="alert(event.type)"/>
HTML事件处理程序的缺点:
1. 存在一个时差问题.因为用户可能会在HTML元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件.通常使用try--catch来捕获异常.
2. 这样扩展事件处理程序的作用域链在不同浏览器中会导致不同的结果.
3. HTML与JavaScript代码紧密耦合,不好修改代码.
2. DOM0级事件处理程序
通过JavaScript指定事件处理程序的传统方法,就是将一个函数赋值给一个事件处理程序的属性.每个元素都有自己的事件处理程序属性.例如:
<html><body>
    <input id="myBtn" type="button" value="Click Me" />
    <!-- 这里onclick必须在按钮初始化完成后-->
    <script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function() {
        alert("Clicked");
        alert(this.id);
    };
</script>
</body></html>
使用DOM0级方法指定的事件处理程序被认为是元素的方法.因此,这时候的事件处理程序是在元素的作用域中运行;则程序中的this引用当前的元素,甚至可以通过this访问元素的任何属性和方法.
我们可以指定属性为空,则删除事件处理程序:
btn.onclick = null; //删除事件处理程序
相比于HTML事件处理程序,JavaScript指定事件处理程序的优势是:
1. 简单.
2. 跨浏览器.
3. DOM2级事件处理程序
DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener().所有DOM节点中都包含这两个方法,并且它们都接受三个参数:要处理的事件名,作为事件处理程序的函数和一个布尔值.最后这个布尔值参数是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序(一般都是冒泡阶段来处理事件):
   <input id="myBtn" type="button" value="Click Me" />
    <!-- 这里onclick必须在按钮初始化完成后-->
    <script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function(){
        alert(this.id);
    }, false);
    </script>
而DOM2级事件的好处是可以添加多个事件处理程序(DOM0级事件处理程序无法办到):
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function(){
        alert(this.id);
    }, false);
    btn.addEventListener("click", function(){
        alert("Hello world!");
    }, false);
</script>
事件处理会按照它们添加的顺序触发.通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时候传入的参数与添加处理程序时使用的参数相同.这就意味着通过addEventListener()添加的匿名函数将无法移除:
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function(){
        alert(this.id);
    }, false);
    //移除失败!因为匿名函数不相等
    btn.removeEventListener("click", function(){
        alert(this.id);
    }, false)
</script>
我们可以修改如下:
<script type="text/javascript">
    var handler = function() {
        alert(this.id);
    };
    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", handler, false);
    //移除失败!因为匿名函数不相等
    btn.removeEventListener("click", handler, false)
</script>
这时候我们单击不会弹出警告按钮,因为事件被移除了.
4. IE事件处理程序
IE提供与DOM类似的两个方法:attachEvent()和detachEvent().它们接受两个参数:事件处理程序名称和事件处理程序函数.它与DOM2级事件处理程序有以下不同:
1. 事件处理程序会在全局作用域中运行(而DOM是在元素的作用域中运行)
2. 添加不同的事件处理程序后,是以相反的顺序被触发的(和DOM是完全相反)
3. 事件处理程序以on开头
<script type="text/javascript">
    var handler = function() {
        alert(this.id);
    };
    var btn = document.getElementById("myBtn");
    btn.attachEvent("onclick", handler);
</script>
5. 跨浏览器的事件处理程序
<script type="text/javascript">
    var EventUtil = {
        addHandler : function(element, type, handler) {
            //DOM2
            if (element.addEventListener) {
                element.addEventListener(type, handler, false);
            } else if (element.attachEvent) {
                element.attachEvent("on" + type, handler);
            } else {
                element["on" + type] = handler;
            }
        },
        removeHandler : function(element, type, handler) {
            //DOM2
            if (element.removeEventListener) {
                element.removeEventListener(type, handler, false);
            } else if (element.detachEvent) {
                element.detachEvent("on" + type, handler);
            } else {
                element["on" + type] = null;
            }
        }
    };
</script>
而我们可以这样使用EventUtil对象:
   var btn = document.getElementById("myBtn");
    var handler = function() {
        alert("clicked");
    };
    EventUtil.addHandler(btn,"click", handler);

    //省略其他代码
    EventUtil.removeHandler(btn, "click", handler);
3. 事件对象
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息,包括导致事件的元素,事件的类型以及其他特定事件相关的信息.
1. DOM中的事件对象
兼容DOM的浏览器会将一个event对象传入到事件处理程序中.无论指定事件处理程序时使用什么方法(DOM0/DOM2),都会传入event对象:
<html><body>
<script type="text/javascript" src="./print.js"></script>
    <!--这里HTML的事件处理会被DOM所覆盖-->
    <input id="myBtn" type="button" value="Click Me" onClick="alert(event.type + ' HTML')"/>
    <!-- 这里onclick必须在按钮初始化完成后-->
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        alert(event.type + " DOM0");
    };
    btn.addEventListener("click", function(event){
        alert(event.type + " DOM2");
    }, false);
</script>
</body></html>
这里程序只会弹出DOM0和DOM2,而HTML的并未弹出.
event对象包含与创建它的特定事件有关的属性和方法.触发的事件类型不一样,可用的属性和方法也不一样.不过,所有事件都会有下表列出的成员:
属性/方法 类型 读/写 说明
bubbles Boolean 只读 表明事件是否冒泡
cancelable Boolean 只读 表明是否可以取消事件的默认行为
currentTarget Element 只读 其事件处理程序当前正在处理事件的那个元素
defaultPrevented Boolean 只读 为true表示已经调用了preventDefault()(DOM3级事件中新增)
detail Integer 只读 与事件相关的细节信息
eventPhase Integer 只读 调用事件处理程序的阶段:1表示捕获阶段,2表示处于目标,3表示冒泡阶段
preventDefault() Function 只读 取消事件的默认行为.如果cancelable是true,则可以使用这个方法
stopImmediatePropagation() Function 只读 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM3级事件中新增)
stopPropagation() Function 只读 取消事件的进一步捕获或冒泡.如果bubbles为true,则可以使用这个方法
target Element 只读 事件的目标
trusted Boolean 只读 为true表示事件是浏览器生成的.为false表示事件是由开发人员通过javascript创建的.(DOM3级事件中新增)
type String 只读 被触发的事件类型
view AbstractView 只读 与事件关联的抽象视图.等同于发生事件的window对象.
提供一个实例:
<html><body>
    <!--这里HTML的事件处理会被DOM所覆盖-->
    <input id="myBtn" type="button" value="Click Me" />
    <!-- 这里onclick必须在按钮初始化完成后-->
<script type="text/javascript">
    function handler(event) {
        debugger;
        document.write(event.type);
    }
    var btn = document.getElementById("myBtn");
    btn.onclick = handler;
</script>
</body></html>
而通过firebug调试时候可以发现:

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标.如果直接将事件处理程序指定给了目标元素,则this,currentTarget和target包含相同的值:
<html><body>
<script type="text/javascript" src="./print.js"></script>
    <!--这里HTML的事件处理会被DOM所覆盖-->
    <input id="myBtn" type="button" value="Click Me"/>
    <!-- 这里onclick必须在按钮初始化完成后-->
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        //this为当前对象
        //currentTarget为事件处理程序当前正在处理事件的元素
        //target为事件的目标
        alert(event.currentTarget === this);  //true
        alert(event.target === this);     //true
    };

    document.body.onclick = function(event) {
        alert(event.currentTarget === document.body); //true
        alert(this === document.body);    //true
        alert(event.target === document.getElementById("myBtn")); //true
    };

</script>
</body></html>
在需要通过一个函数处理多个事件时,可以使用type属性:
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    var handler = function(event) {
        switch (event.type) {
            case "click":
                alert("clicked");
                break;
            case "mouseover":
                event.target.style.backgroundColor = "red";
                break;
            case "mouseout":
                event.target.style.backgroundColor = "";
                break;
        }
    };

    btn.onclick = handler;
    btn.onmouseover = handler;
    btn.onmouseout = handler;
</script>
则鼠标移动到按钮上时候,会显示红色.移开则恢复默认值.点击则弹出警告框.
我们可以通过preventDefault()来阻止特定事件的默认行为:
   //阻止默认导航
    var link = document.getElementById("myLink");
    link.onclick = function(event) {
        event.preventDefault();
    };
我们也可以使用stopPropagation()来停止事件在DOM层次中的传播:
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        alert("Clicked");
        event.stopPropagation();    //如果注释掉,则body会弹出警告框
    };
    document.body.onclick = function(event) {
        alert("Body clicked");
    };
</script>
2. 跨浏览器的事件对象
var EventUtil = {
        addHandler : function(element, type, handler) {
            //DOM2
            if (element.addEventListener) {
                element.addEventListener(type, handler, false);
            } else if (element.attachEvent) {
                element.attachEvent("on" + 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) {
            //DOM2
            if (element.removeEventListener) {
                element.removeEventListener(type, handler, false);
            } else if (element.detachEvent) {
                element.detachEvent("on" + type, handler);
            } else {
                element["on" + type] = null;
            }
        },
        stopPropagation : function(event) {
            if (event.stopPropagation) {
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            }
        }
};
4. 事件类型
1. UI事件
1. load事件
当页面完全加载后在window上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在<img>元素上面触发,或者当嵌入的内容加载完毕时在<object>元素上面触发.一般有两种指定onload事件处理程序的方式:第一种是JavaScript方式(推荐使用):
EventUtil.addHandler(window, "load", function(event){
    alert("load window success!");
});
第二种方式是在<body>元素添加一个onload特性(因为HTML中无法访问window元素):
<body onload="alert('load body success!')">
而我们甚至可以在图像上触发load事件,具体实例如下:
<html>
<script type="text/javascript" src="./print.js"></script>
<script type="text/javascript" src="./EventUtil.js"></script>
<!--body元素添加onload特性-->
<body onload="alert('load body success!')">

<p>example for load event</p>
<img id="myImage" src="smile.gif" onload="alert('load image success using HTML!')" />
<script type="text/javascript">
    //整个页面加载成功后提示
    EventUtil.addHandler(window, "load", function(event){
        alert("load window success!");
    });

    //加载图像
    var image = document.getElementById("myImage");
    EventUtil.addHandler(image, "load", function(event){
        event = EventUtil.getEvent(event);
        alert(EventUtil.getTarget(event).src);
    });

</script>

</body></html>
界面依次弹窗显示:
load image success using HTML!
file:///C:/Users/fzyz_sb/Desktop/smile.gif
load body success!
load window success!

但是,为什么下面的页面会卡住呢?
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>example for load event</title>
        <script type="text/javascript" src="./EventUtil.js"></script>
    </head>
    <body onload="showBodyLoad()">
    <img id="myImage" src="smile.gif" onload="showImageLoad()" />

    <script type="text/javascript">
        function print(str) {
            document.write(str + "<br />");
        }

        function showBodyLoad() {
            print("show body load");
        }

        function showImageLoad() {
            print("show image load");
        }

        EventUtil.addHandler(window, "load", function(event){
            print("load window success");
        });

        //加载图片
        var image = document.getElementById("myImage");
        EventUtil.addHandler(image, "load", function(event){
            event = EventUtil.getEvent(event);
            print(EventUtil.getTarget(event).src);
        });

    </script>
</body></html>
界面显示:
show image load
后一直在加载.......

而我们可以为<script>和<link>指定事件处理程序:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>example for load event</title>
        <script type="text/javascript" src="./EventUtil.js"></script>
    </head>
    <body>

    <script type="text/javascript">

        //为<script>元素指定事件处理程序
    EventUtil.addHandler(window, "load", function(){
        var script = document.createElement("script");
        EventUtil.addHandler(script, "load", function(event){
            alert("Loaded");
        });
        script.src = "print.js";
        document.body.appendChild(script);
    });
    //为<link>指定事件处理程序
    EventUtil.addHandler(window, "load", function(){
        var link = document.createElement("link");
        link.type = "text/css";
        link.rel = "stylesheet";
        EventUtil.addHandler(link, "load", function(event){
            alert("css loaded");
        });
        link.href = "style.css";
        document.getElementsByTagName("head")[0].appendChild(link);
    });

    </script>
</body></html>
界面一次弹出:
css loaded
Loaded
2. unload事件
当页面完全卸载后在window上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在<object>元素上面触发.
<script type="text/javascript">
        EventUtil.addHandler(window, "unload", function(event){
            alert("Unloaded");
        });
</script>
但如何编写此实例呢?
3. resize事件
当窗口或框架的大小变化时在window或框架上面触发.
<script type="text/javascript">
    EventUtil.addHandler(window, "resize", function(event){
        alert("resize window success!");
    });
</script>
但是为什么resize会被触发两次呢?
4. scroll事件
当用户滚动带滚动条的元素中的内容时,在该元素上面触发.<body>元素中包含所加载页面的滚动条.
<script type="text/javascript">
    EventUtil.addHandler(window, "scroll", function(event){
        if (document.compatMode == "CSS1Compat") {
            alert(document.documentElement.scrollTop);
        } else {
            alert(document.body.scrollTop);
        }
    });
    for (var i = 0; i < 100; i++) {
        print("hello world");
    }
</script>
与resize事件类似,scroll事件也会在文档被滚动期间重复触发,所以有必要尽量保持事件处理程序的代码简单.
2. 焦点事件
焦点事件会在页面元素获得或失去焦点.有用的四个焦点事件:
1. blur:在元素失去焦点时触发.这个事件不会冒泡.
2. focus:在元素获得焦点时触发.这个事件不会冒泡.
3. focusin:在元素获得焦点时触发,与focus等价,但是冒泡.
4. focusout:在元素失去焦点时触发,与blur等价.
实例如下:
1. blur和focus:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>example for load event</title>
    </head>
    <body>
    <input id="myBtn" type="button" value="click me" />
    <script type="text/javascript">
    var myBtn = document.getElementById("myBtn");

    myBtn.addEventListener("focus", function(event){
        alert("focus button");
    }, false);

    myBtn.addEventListener("blur", function(event){
        alert("blur button");
    }, false);
    </script>
</body></html>
这里需要注意:blur会比focus先触发.如果一一测试(即测试focus时候屏蔽blur,测试blur时候屏蔽focus),则可以看到效果.
3. 鼠标与滚轮事件
DOM3级事件定义了9个鼠标事件:
1. click:单击鼠标或按下回车键.
2. dblclick:双击主鼠标(一般是左鼠标)
3. mousedown:按下任意鼠标按钮
4. mouseenter:在鼠标光标从元素外部首次移动到元素范围内时触发.这个事件不冒泡.
5. mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发.这个事件不冒泡.
6. mousemove:当鼠标指针在元素内部移动时重复的触发.不能通过键盘触发这个事件.
7. mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发.
8. mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发.
9. mouseup:在用户释放鼠标按钮时触发.不能通过键盘触发这个事件.
1. 客户区坐标位置/页面坐标位置/屏幕坐标位置
鼠标事件都是在浏览器视口中的特定位置上发生的,这个位置信息保存在事件对象的clientX和clientY属性中,它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标.
<div id="myDiv" style="background-color:red;height:100px;width:100px">Click me</div>
<script type="text/javascript">
    var div = document.getElementById("myDiv");
    EventUtil.addHandler(div, "click", function(event){
        event = EventUtil.getEvent(event);
        alert("X:" + event.clientX + ",Y:" + event.clientY);
    });
</script>
当我们单击div内部时,会弹出坐标值:

而对于页面来说(存在滚轮的情况下),应该使用pageX和pageY.而对于整个屏幕而言,应该使用screenX和screenY:
<div id="myDiv" style="background-color:red;height:1000px;width:1000px">Click me</div>
<script type="text/javascript">
    var div = document.getElementById("myDiv");
    EventUtil.addHandler(div, "click", function(event){
        event = EventUtil.getEvent(event);
        alert("X:" + event.clientX + ",Y:" + event.clientY);
        alert("PageX:" + event.pageX + ",PageY:" + event.pageY);
        alert("ScreenX:" + event.screenX + ",ScreenY:" + event.screenY);
    });
</script>
滚动鼠标到页面底部位置,单击即可看到坐标均不同.
2. 修改键
我们可以通过shiftKey, ctrlKey,altKey和metaKey来判断Shift,Ctrl,Alt和Meta键是否被按下:
<div id="myDiv" style="background-color:red;height:1000px;width:1000px">Click me</div>
<script type="text/javascript">
    var div = document.getElementById("myDiv");
    EventUtil.addHandler(div, "click", function(event){
        event = EventUtil.getEvent(event);
        var keys = new Array();

        if (event.shiftKey) {
            keys.push("shift");
        }
        if (event.ctrlKey) {
            keys.push("ctrl");
        }
        if (event.altKey) {
            keys.push("alt");
        }
        if (event.metaKey) {
            keys.push("meta");
        }

        alert("Keys:" + keys.join(","));
    });
</script>
而浏览器响应如下:

3. 相关元素
对于mouseover和mouseout事件而言,会设计更多的元素.对mouseover事件而言,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素.类似的,对于mouseout事件而言,事件的主目标是失去光标的元素,而相关元素则是获得光标的元素.
<div id="myDiv" style="background-color:red;height:100px;width:100px">Click me</div>
在页面上会显示一个<div>元素.如果鼠标指针一开始位于这个<div>元素上,然后移出了这个元素,那么就会在<div>元素上触发mouseout事件,相关元素就是<body>元素.与此同时,<body>元素上面会触发mouseover事件,而相关元素变成了<div>.
我们可以通过属性relatedTarget来获取元素的信息:
<script type="text/javascript">
    var div = document.getElementById("myDiv");
    EventUtil.addHandler(div, "mouseout", function(event){
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        var relatedTarget = EventUtil.getRelatedTarget(event);
        alert("moused out of " + target.tagName + " to " + relatedTarget.tagName);
    });
</script>
4. 键盘与文本事件
有三个键盘事件:
1. keydown:按下键盘上面的任意键时候触发,如果按住不放会重复触发.
2. keypress:按下键盘上的字符键时触发,如果按下不放会重复触发.
3. keyup:释放键盘上的键时触发.
一个文本事件:textInput.在文本插入文本框之前会触发textInput事件.
1. 键码:当发生keydown时,event对象的keyCode属性会包含一个代码,与特定的键对应.
2. 字符编码:为charCode
但是一般情况下,keyCode和charCode不可同时拥有值,所以会做出如下的判断:
    getCharCode: function(event){
        if (typeof event.charCode == "number"){
            return event.charCode;
        } else {
            return event.keyCode;
        }
    },
实例如下:
<input id="myText" type="text" value="input" />
    <script type="text/javascript">
    function print(str) {
        document.write(str + "<br />");
    }
    var myText = document.getElementById("myText");
    EventUtil.addHandler(myText, "keyup", function(event){
        event = EventUtil.getEvent(event);
        alert(event.keyCode);
    });

    </script>
每次输入字符,会弹出其ASCII码.

而DOM3级事件引入了一个新事件:textInput.它和keypress的textInput事件的行为不同在于两点:
1. 任何可以获得焦点的元素都可以触发keypress事件,但只有可编辑区域才能触发textInput事件.
2. textInput事件只会在用户按下能够输入实际字符的键时才会被触发,而keypress事件则在按下那些能够影响文本显示的键时也会触发(例如退格键)
var myText = document.getElementById("myText");
    EventUtil.addHandler(myText, "textInput", function(event){
        event = EventUtil.getEvent(event);
        alert(event.data);
    });
备注:firefox不支持此事件(貌似还没有浏览器支持它).
5. 变动事件
变动事件是为XML或HTML DOM设计的:
1. DOMSubtreeModified:在DOM结构中发生任何变化时触发.这个事件在其他任何事件触发后都会触发.
2. DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中触发.
3. DOMNodeRemoved:在节点从其父节点中被移除时触发.
4. DOMNodeInsertedIntoDocument:在一个节点被直接插入文档或通过子树间接插入文档之后触发.这个事件在DOMNodeInserted之后触发.
5. DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发.这个事件在DOMNodeRemoved之后触发.
6. DOMAttrModified:在特性被修改之后触发.
7. DOMCharacterDataModified:在文本节点的值发生变化时触发.
1. 删除节点
以以下HTML页面为例:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>Node Removal Events Example</title>
        <script type="text/javascript" src="./EventUtil.js"></script>
    </head>
    <body>
        <ul id="myList">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 3</li>
        </ul>
    </body>
    <script type="text/javascript">
    function print(str) {
        document.write(str + "<br />");
    }
        EventUtil.addHandler(window, "load", function(event){
            var list = document.getElementById("myList");

            EventUtil.addHandler(document, "DOMSubtreeModified", function(event){
                print("---DOMSubtreeModified");
                print(event.type);
                print(event.target);
            });

            EventUtil.addHandler(document, "DOMNodeRemoved", function(event){
                print("---DOMNodeRemoved");
                print(event.type);
                print(event.target);
                print(event.relatedNode);
            });

            EventUtil.addHandler(document, "DOMNodeRemovedFromDocument", function(event){
                print("---DOMNodeRemovedFromDocument");
                print(event.type);
                print(event.target);
            });

            list.parentNode.removeChild(list);
        });
    </script>
</html>
要移除<ul>元素,会依次触发以下事件:

1. 在<ul>元素上触发DOMNodeRemoved事件.relatedNode属性等于document.body.
2. 在<ul>元素上触发DOMNodeRemovedFromDocument事件.
3. 在身为<ul>元素子节点的每个<li>元素及文本节点上触发DOMNodeRemovedFromDocument事件.
4. 在document.body上触发DOMSubtreeModefied事件,因为<ul>元素是document.body的直接子元素.
可以,界面上就触发了DOMNodeRemoved事件,这到底是为什么?
浏览器显示如下:
---DOMNodeRemoved
DOMNodeRemoved
[object HTMLUListElement]
[object HTMLBodyElement]
2. 插入节点
与上面类似:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>Node Removal Events Example</title>
        <script type="text/javascript" src="./EventUtil.js"></script>
        <script type="text/javascript">
    function print(str) {
        document.write(str + "<br />");
    }
        EventUtil.addHandler(window, "load", function(event){
            var list = document.getElementById("myList");
            var item = document.createElement("li");
            item.appendChild(document.createTextNode("Item 4"));

            EventUtil.addHandler(document, "DOMSubtreeModified", function(event){
                alert("---DOMSubtreeModified");
                alert(event.type);
                alert(event.target);
            });

            EventUtil.addHandler(document, "DOMNodeInserted", function(event){
                alert("---DOMNodeRemoved");
                alert(event.type);
                alert(event.target);
                alert(event.relatedNode);
            });

            EventUtil.addHandler(document, "DOMNodeInsertedIntoDocument", function(event){
                alert("---DOMNodeRemovedFromDocument");
                alert(event.type);
                alert(event.target);
            });

            list.parentNode.removeChild(list);
        });
    </script>
    </head>
    <body>
        <ul id="myList">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 3</li>
        </ul>
    </body>

</html>
6. HTML5事件
1. contextmenu事件
上下文菜单事件:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>ContextMenu Event Example</title>
        <script type="text/javascript" src="./EventUtil.js"></script>
        <script type="text/javascript">
        EventUtil.addHandler(window, "load", function(event){
            var div = document.getElementById("myDiv");

            EventUtil.addHandler(div, "contextmenu", function(event){
                event = EventUtil.getEvent(event);
                EventUtil.preventDefault(event);

                var menu = document.getElementById("myMenu");
                menu.style.left = event.clientX + "px";
                menu.style.top = event.clientY + "px";
                menu.style.visibility = "visible";
            });

            EventUtil.addHandler(document, "click", function(event){
                document.getElementById("myMenu").style.visibility = "hidden";
            });
        });

    </script>
    </head>
    <body>
        <div id="myDiv">click me</div>
        <ul id="myMenu" style="position: absolute;visibility: hidden;background-color: silver">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 3</li>
        </ul>
    </body>

</html>
2. beforeunload事件
为了让开发人员有可能在页面卸载前阻止这一操作而定义了beforeunload,当然,这个事件是通知用户界面将被卸载(但是具体如何举例依旧不知道):
EventUtil.addHandler(window, "beforeunload", function(event){
    event = EventUtil.getEvent(event);
    var message = "really goodbye!";
    event.returnValue = message;
        return message;
});
3. DOMContentLoaded事件
此事件则在形成完整的DOM树之后会被触发.
5. EventUtil.js源代码
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 {
            element["on" + type] = handler;
        }
    },

    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;
            }
        }
    },

    getCharCode: function(event){
        if (typeof event.charCode == "number"){
            return event.charCode;
        } else {
            return event.keyCode;
        }
    },

    getClipboardText: function(event){
        var clipboardData =  (event.clipboardData || window.clipboardData);
        return clipboardData.getData("text");
    },

    getEvent: function(event){
        return event ? event : window.event;
    },

    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;
        }

    },

    getTarget: function(event){
        return event.target || event.srcElement;
    },

    getWheelDelta: function(event){
        if (event.wheelDelta){
            return (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta);
        } else {
            return -event.detail * 40;
        }
    },

    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("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    },

    setClipboardText: function(event, value){
        if (event.clipboardData){
            event.clipboardData.setData("text/plain", value);
        } else if (window.clipboardData){
            window.clipboardData.setData("text", value);
        }
    },

    stopPropagation: function(event){
        if (event.stopPropagation){
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }

};












猜你喜欢

转载自zhyp29.iteye.com/blog/2304402