IE的事件流叫作事件冒泡,即事件开始时由最具体的元素接收,而后逐级向上传播到较为不具体的节点(文档)。以下代码:css
<body> <div id="myDiv">click me</div> </body>
若是你点击了上面的div元素,那么这个click事件会按照以下顺序传播:html
全部现代浏览器都支持事件冒泡,但在具体实现上仍是有一些差异。IE5.5以及更早的版本事件冒泡会跳过<html>元素(从<body>直接跳到document)。IE9及其它浏览器则将事件一直冒泡到window对象。web
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。如上面的例子,单机div元素,那么就会按照如下顺序触发click事件:windows
因为老版本浏览器的不支持,所以不多有人使用事件捕获。数组
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。浏览器
<input type="button" value="click me" onclick="alert(this.value)"/>
var btn = document.getElementById('myBtn'); btn.onclick = function(){ console.log(this.value); }
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操做:addEventListener()和removeEventListener()。接受三个参数:要处理的事件名、做为事件处理程序的函数和一个布尔值。布尔值为true,表示在捕获阶段调用事件处理程序;若是为false,表示在冒泡阶段调用事件处理程序。缓存
var btn = document.getElementById('myBtn'); btn.addEventListener('click',function(){ alert(this.value) },false)
IE中实现了与DOM中相似的两个方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称和事件处理程序函数。因为IE8及更早版本只支持事件冒泡,因此经过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。app
btn.attachEvent('onclick',function(){ alert(this.value) //undefined })
结果为undefined的缘由是,在使用attachEvent()方法的状况下,事件处理程序会在全局做用域中运行。所以this等于window。在编写跨浏览器区别的时候,牢记这一区别很是重要。框架
attachEvent()方法也能够为一个元素添加多个事件处理程序,以下:iphone
btn.attachEvent('onclick',function(){ alert('clicked') }) btn.attachEvent('onclick',function(){ alert('hello world!') })
在IE9以及更改版本浏览器获得的结果顺序跟addEventListener()的顺序同样,结果是先clicked,后hello world!。在IE8以及之前版本获得顺序是相反的。
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; } }, 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; } } }
调用方式:
var handler = function(){ alert('clicked'); } EventUtil.addHandler(btn,"click",handler); //这里省略其它代码 EventUtil.removeHandler(btn,"click",handler);
兼容DOM的浏览器会将一个event对象传入到事件处理程序中,不管指定事件处理程序时用什么方法(DOM0级或DOM2级),都会传入event对象。下面的例子:
var btn = document.getElementById('myBtn'); btn.onclick = function(event){ console.log(event.type); //click } btn.addEventListener('click',function(event){ console.log(event.type); //click },false)
在经过HTML特性指定事件处理程序时,变量event中保存着event对象,下面例子:
<input type="button" value="click me" onclick="alert(event.type)"/>
event对象包含与建立它的特定事件有关的属性和方法。触发的事件类型不同,可用的属性和方法也不同。不过,全部事件都会有下表列出的成员:
在事件处理程序内部,对象this始终等于currentTarget的值,而target只包含事件的实际目标。若是直接将事件处理程序指向给了目标元素,则this,currentTarget和target包含相同的值,以下代码:
var btn = document.getElementById('myBtn'); btn.addEventListener('click',function(event){ console.log(event.currentTarget === this); //true console.log(event.target === this); //true },false)
若是事件处理程序存在于按钮的父节点中(例如document.body),那么这些值是不相等的,以下代码:
var btn = document.getElementById('myBtn'); document.body.addEventListener('click',function(event){ console.log(event.currentTarget === document.body); //true console.log(document.body === this); //true console.log(event.target === btn); //true },false)
在须要一个函数处理多个事件时,可使用type属性,以下:
var btn = document.getElementById('myBtn'); var handler = function(event){ switch(event.type){ case "click": console.log("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;
阻止事件的默认行为,使用preventDefault()方法,好比阻止a标签的跳转,以下代码:
var link = document.getElementById('myLink'); link.onclick = function(event){ event.preventDefault(); }
只有cancelable属性设置为true时,才可使用preventDefault()来取消其默认行为。
stopPropagation()方法用于当即中止事件在DOM层次中的传播,寄取消进一步的事件捕获或冒泡。例如直接添加到一个按钮的事件处理程序能够调用stopPropagation(),从而避免触发注册在document.body上面的事件处理程序,以下代码:
var btn = document.getElementById('myBtn'); btn.onclick = function(event){ console.log('clicked'); event.stopPropagation(); } document.body.onclick = function(){ console.log('body clicked'); }
结果只打印出了clicked。
事件对象的eventPhase属性,能够用来肯定事件当前正处理事件流的哪一个阶段。1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段。
var btn = document.getElementById('myBtn'); btn.onclick = function(event){ console.log(event.eventPhase);//2 }; document.body.addEventListener('click',function(event){ console.log(event.eventPhase);//1 },true); document.body.onclick = function(){ console.log(event.eventPhase);//3 };
显示结果顺序分别为1,2,3。
与访问DOM中的event对象不一样,要访问IE中的event对象有几种不一样的方式,取决于事件处理程序的方法。在使用DOM0级方法添加事件处理程序时,event对象做为window对象的一个属性存在,以下例子:
var btn = document.getElementById('myBtn'); btn.onclick = function(){ var event = window.event; alert(event.type); //click };
若是事件程序是使用attachEvent()添加的,那么就会有一个event对象做为参数被传递到事件处理程序函数中,以下代码:
var btn = document.getElementById('myBtn'); btn.attachEvent('onclick',function(event){ alert(event.type); //click })
IE中的event对象包含下面的属性和方法:
由于事件处理程序中的做用域是根据指定它的方式来肯定的,因此不能认为this会始终等于事件目标。故而,最好仍是使用event.srcElement比较保险,以下代码:
var btn = document.getElementById('myBtn'); btn.onclick = function(){ alert(window.event.srcElement === this); //true }; btn.attachEvent('onclick',function(event){ alert(this); //window alert(window.event.srcElement === this); //false })
取消默认行为:
var link = document.getElementById('myLink'); link.onclick = function(){ window.event.returnValue = false; };
中止事件冒泡:
var btn = document.getElementById('myBtn'); btn.onclick = function(){ alert('clicked'); window.event.cancelBubble = true; }; document.body.attachEvent('onclick',function(event){ alert('body clicked') })
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; } }, //获得event对象 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("on" + type,handler); }else{ element["on" + type] = null; } }, //阻止事件捕获或冒泡 stopPropagation:function(event){ if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true; } } }
“DOM3级事件”规定以下几类事件。
多数这些事件都与window对象或者表单控件相关。
除了DOMActivate以外,其它事件在DOM2级事件中都归为HTML事件(DOMActivate在DOM2级事件中仍属于UI事件)。要肯定浏览器是否支持DOM2级事件规定的HTML事件,能够用以下代码:
var isSupported = document.implementation.hasFeature("HTMLEvents","2.0");
肯定浏览器是否支持DOM3级事件定义的事件,代码以下:
var isSupported = document.implementation.hasFeature("UIEvent","3.0");
EventUtil.addHandler(window,'load',function(event){ console.log('loaded!'); })
为<body>元素添加一个onload特性,代码以下:
<body onload="alert('loaded!')"> </body>
通常在window上面发生的任何事件均可以在<body/>元素中经过相应的特性来指定,由于在HTML中没法访问到window元素。建议尽量使用JavaScript方式。
图片加载:
var image = document.getElementById('myImage'); EventUtil.addHandler(image,'load',function(event){ event = EventUtil.getEvent(event); console.log(EventUtil.getTarget(event).src); })
待建立新的<img>元素时,能够为其指定一个事件处理程序。此时,最重要的是在指定src属性以前先指定事件,以下代码:
EventUtil.addHandler(window,'load',function(){ var image = document.createElement('img'); EventUtil.addHandler(image,'load',function(event){ event = EventUtil.getEvent(event); console.log(EventUtil.getTarget(event).src); }) document.body.appendChild(image); image.src = 'images/b.jpg'; })
须要格外注意的一点是:新图像元素不必定要从添加到文档后才开始下载,只要设置了src属性就开始下载。
一样的功能可使用DOM0级的Image对象实现,以下代码:
EventUtil.addHandler(window,'load',function(){ var image = new Image(); EventUtil.addHandler(image,'load',function(event){ event = EventUtil.getEvent(event); console.log(EventUtil.getTarget(event).src); }) image.src = 'images/b.jpg'; })
还有一些元素以非标准的的方式支持load事件。在IE9以及更高版本,<script>元素也会触发load事件。
EventUtil.addHandler(window,'load',function(){ var script = document.createElement('script'); EventUtil.addHandler(script,'load',function(event){ console.log('loaded!'); }) script.src = 'js/common.js'; document.body.appendChild(script); })
注:IE8以及更早版本不支持<script>元素上的load事件。
<link>元素的load事件:
EventUtil.addHandler(window,'load',function(){ var link = document.createElement('link'); link.type ="text/css"; link.rel ="stylesheet"; EventUtil.addHandler(link,'load',function(event){ console.log('loaded!'); }) link.href = 'css/rest.css'; document.getElementsByTagName('head')[0].appendChild(link) })
与<script>节点相似,在未指定href属性并将<link>元素添加到文档以前也不会开始下载样式表。
这个事件在文档彻底被卸载后触发。只要用户从一个页面切换到另外一个页面,就会发生unload事件。而利用这个事件最多的状况就是清除引用,以免内存泄露。
EventUtil.addHandler(window,'unload',function(){ alert('unloaded!'); })
EventUtil.addHandler(window,'resize',function(){ alert('resized!'); })
EventUtil.addHandler(window,'scroll',function(){ if(document.compatMode == 'CSS1Compat'){ console.log(document.documentElement.scrollTop); }else{ console.log(document.body.scrollTop); } })
焦点事件会在页面得到或者失去焦点时触发。利用这些事件并与document.hasFocus()方法以及document.activeElement属性配合,能够知晓用户在页面中的行踪,如下6个焦点事件。
IE的focusin和focusout最后被DOM3级事件采纳为标准方式。
当焦点从页面中的一个元素移动到另外一个元素,会依次触发下列事件:
(1)focusout在失去焦点的元素上触发。
(2)focusin在得到焦点的元素上触发。
(3)blur在失去焦点的元素上触发。
(4)DOMFocusOut在失去焦点的元素上触发。
(5)focus在得到焦点的元素上触发。
(6)DOMFocusIn在得到焦点的元素上触发。
肯定浏览器是否支持这些事件,可使用以下代码:
var isSupported = document.implementation.hasFeature('FocusEvent','3.0');
DOM3级事件中定义了9个鼠标事件,以下:
页面上的全部元素都支持鼠标事件。除了mouseenter和mouseleave,全部鼠标事件都会冒泡,也能够被取消,而取消鼠标事件将会影响浏览器的默认行为。取消鼠标事件的默认行为还会影响其余事件,由于鼠标事件和其余事件是密不可分的关系。
只有在同一个元素上相继触发mousedown和mouseup事件,才会触发click事件;若是mousedown或mouseup中的一个被取消,就不会触发click事件。相似地,只有触发两次click事件,才会触发一次dblclick事件,若是有代码阻止了连续两次触发click事件(多是直接取消click事件,也可能经过取消mousedown或mouseup间接实现),那么就不会触发dblclick事件。这4个事件触发的顺序以下:
(1)mousedown
(2)mouseup
(3)click
(4)mousedown
(5)mouseup
(6)click
(7)dblclick
IE8及以前版本的实现有一个小bug,所以在双击事件中,会跳过第二个mousedown和click事件,其顺序以下:
(1)mousedown
(2)mouseup
(3)click
(4)mouseup
(5)dblclick
IE9修复了这个bug。
使用以下代码能够检测浏览器是否支持如上DOM2级事件(除dblckick、mouseenter和mouseleave以外):
var isSupported = document.implementation.hasFeature('MouseEvents','2.0');
检测浏览器是否支持上面的全部事件,代码以下:
var isSupported = document.implementation.hasFeature('MouseEvent','3.0');
clientX和clientY他们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标。
var btn = document.getElementById('myBtn'); EventUtil.addHandler(btn,'click',function(event){ event = EventUtil.getEvent(event); console.log("client coordinates:" + event.clientX + "," + event.clientY); })
pageX和pageY,这两个属性表示鼠标光标在页面中的位置,所以坐标是从页面自己而非视口的左边和顶边计算的。
如下代码能够取得鼠标事件在页面中的坐标:
var btn = document.getElementById('myBtn'); EventUtil.addHandler(btn,'click',function(event){ event = EventUtil.getEvent(event); console.log("Page coordinates:" + event.pageX + "," + event.pageY); })
在页面没有滚动的状况下,pageX和pageY的值与clientX和clientY的值相等。
IE8及更早的版本不支持事件对象上的页面坐标,不过可使用客户区坐标和滚动信息能够计算出来。这时候须要用到document.body(混杂模式)或document.documentElement(标准模式)中的scrollLeft和scrollTop属性。计算代码以下:
var btn = document.getElementById('myBtn'); EventUtil.addHandler(btn,'click',function(event){ event = EventUtil.getEvent(event); var pageX = event.pageX, pageY = event.pageY; if(pageX === undefined){ pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft); } if(pageY === undefined){ pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop); } console.log("Page coordinates:" + pageX + "," + pageY); })
screenX和screenY属性表示鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。
屏幕坐标:
var btn = document.getElementById('myBtn'); EventUtil.addHandler(btn,'click',function(event){ event = EventUtil.getEvent(event); console.log("Page coordinates:" + event.screenX + "," + event.screenY); })
虽然鼠标事件主要是由鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也能够影响到所要采起的操做。这些修改键就是Shift、Ctrl、Alt和Meta(在windows键盘中的windows键,在苹果机中是Cmd键),它们常常被用来修改鼠标事件的行为。DOM为此规定了4个属性,表示这些修改键的状态:shiftKey、ctrlKey、altKey、metaKey。这些属性中包含的都是布尔值,若是相应的键被按下了,则值为true,不然值为false。
当某个鼠标事件发生时,经过检测这几个属性能够肯定是否用户同时按下了其中的键。以下例子:
var btn = document.getElementById('myBtn'); EventUtil.addHandler(btn,'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'); } console.log("keys:" + keys.join(',')); })
注:IE8以及以前的版本不支持metaKey属性。
在发生mouseover和mouseout事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指针从一个元素的边界以内移动到另外一个元素的边界以内。对mouseover而言,事件的主目标是得到光标的元素,而相关元素就是那个失去光标的元素。相似地,对于mouseout事件而言,事件的主目标就是失去光标的元素,而相关元素是得到光标的元素。来看下面一个例子:
<body> <div id="myDiv" style="background-color:red;width:100px;height:100px;"></div> </body>
这个例子会在页面上显示一个<div>元素。若是鼠标指针一开始就在这个<div>元素上,而后移出了这个元素,那么就会在<div>元素上触发mouseout事件,相关元素就是<body>元素。与此同时,<body>元素上面会触发mouseenter事件,相关元素就变成了<div>。
DOM经过event对象的relatedTarget属性提供了相关元素的信息。这个属性只对于mouseover和mouseout事件才包含值;对于其它事件,这个属性的值为null。IE8以及以前的版本不支持relatedTarget属性,但提供了保存着一样信息的不一样属性。在mouseover事件触发时,IE中的fromElement属性中保存了相关元素;在mouseout事件触发时,IE的toElement属性保存着相关元素。(IE9支持这些全部属性。)把这个添加到EventUtil对象中,以下:
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; } }, //省略了其它代码 };
调用:
var myDiv = document.getElementById('myDiv'); EventUtil.addHandler(myDiv,'mouseout',function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); var relateTarget = EventUtil.getRelatedTarget(event); console.log("moused out of" + target.tagName + "to" + relateTarget.tagName); //moused out ofDIVtoBODY })
只有在主鼠标按钮被单击(或键盘回车键被按下)时才会触发click事件,所以检测按钮的信息不是必要的。但对于mousedown和mouseup事件来讲,则在其event对象存在一个button属性,表示按下或者释放的按钮。DOM的button属性可能有以下3个值:
在常规的设置中,主鼠标按钮就是鼠标左键,而次鼠标按钮就是鼠标右键。
IE8及以前的版本也提供了button属性,但这个属性的值与DOM中的button属性有很大的差别。
因为单独使用能力检测没法肯定差别(两种模型有同名的button属性),所以必须另辟蹊径。咱们知道,支持DOM版鼠标事件的浏览器课能够经过hasFearture()方法检测,全部能够再为EventUtil对象添加getButton()方法:
//获得button属性 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; } } },
调用:
var myDiv = document.getElementById('myDiv'); EventUtil.addHandler(myDiv,'mousedown',function(event){ event = EventUtil.getEvent(event); alert(EventUtil.getButton(event)); })
“DOM2级事件”规范在event对象中还提供了detail属性,用于给出有关事件的更多信息。对于鼠标事件来讲,detail中包含了一个数值,表示在给定位置上发生了多少次单击。在同一个像素上相继的发生一次mousedown和一次mouseup事件算做一次单击。detail属性从1开始计数,每次单击发生后都会递增。若是鼠标在mousedown和mouseup之间移动了位置,则detail会被重置为0。
IE也经过下列属性为鼠标事件提供了更多信息。
这些属性用处不大,只有IE支持他们,另外一方面他们提供的信息要么没有什么用,要么能够经过其余方式计算得来。
IE6.0首先实现了mousewheel事件。这个事件能够在任何元素上面触发,最终会冒泡到document(IE8)或window(IE九、Opera、Chrome及Safari)对象。与mousewheel事件对应的event对象除了包含鼠标事件的全部标准信息外,还包含一个特殊的wheelDelta属性。当用户向前滚动鼠标滑轮时,wheelDelta是120的倍数;当用户向后滚动鼠标滑轮时,wheelDelta是-120的倍数。
EventUtil.addHandler(document,'mousewheel',function(event){ event = EventUtil.getEvent(event); console.log(event.wheelDelta); })
多数状况下,只须要知道滚轮的滚动方向就够了,而这经过检测wheelDelta的正负号就能够肯定。
注意的是,在Opera9.5以前的版本中,wheelDelta值的正负号是颠倒的。若是须要支持早期的Opera版本,代码以下:
EventUtil.addHandler(document,'mousewheel',function(event){ event = EventUtil.getEvent(event); var delta = (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta); console.log(delta); })
Firefox支持一个名为DOMMouseScroll的相似事件,也是在鼠标滚轮滚动时触发。鼠标滚轮滚动信息保存在detail属性中,当向前滚动鼠标滚轮时,这个属性的值为-3的整数倍,当向后滚动鼠标滚轮时,这个属性的值是3的整数倍。
EventUtil.addHandler(document,'DOMMouseScroll',function(event){ event = EventUtil.getEvent(event); console.log(event.detail); })
跨浏览器总结,添加到EventUtil对象中:
//取得鼠标滚轮增量值(delta) getWheelDelta:function(event){ if(event.wheelDelta){ return event.wheelDelta; }else{ return -event.detail * 40; } },
调用方式:
(function(){ function handleMouseWheel(event){ event = EventUtil.getEvent(event); var delta = EventUtil.getWheelDelta(event); console.log(delta); }; EventUtil.addHandler(document,"mousewheel",handleMouseWheel); EventUtil.addHandler(window,"DOMMouseScroll",handleMouseWheel); })();
IOS和Android的实现很是特别,由于这些设备没有鼠标。在面向iphone和ipad中的Safari开发时,要记住如下几点:
若是你的web应用程序或者网站要确保残疾人特别是那些使用屏幕阅读器的人都能访问,那么在使用鼠标事件时就要格外当心。前面提到过,能够经过键盘上的回车键来触发click事件,但其余鼠标事件却没法经过键盘来触发。为此,咱们不建议使用click以外的其余鼠标事件来展现功能或者引起代码执行。由于这样会给盲人或视障用户形成极大不便。
“DOM3级事件”为键盘事件制定了规范。有3个键盘事件以下:
只有一个文本事件:textInput。这个事件是对keypress的补充,用意是在将文本显示给用户以前更容易拦截文本。在文本插入文本框以前会触发textInput事件。
在用户按下键盘上的字符键时,键盘执行顺序:keydown、keypress、keyup。其中keydown、keypress都是在文本框发生变化以前被触发的;而keyup是在文本框已经发生变化后触发。若是用户按下一个字符键不放,那么会重复触发keydown与keypress,直到用户松开该键为止。
若是用户按下的是一个非字符键时,执行顺序:keydown、keyup。
在发生keydown与keyup事件时,event对象的keyCode属性包含一个代码,与键盘上一个特定的键对应。DOM和IE中的event对象都支持keyCode属性。以下例子:
var textbox = document.getElementById('myText'); EventUtil.addHandler(textbox,'keyup',function(event){ event = EventUtil.getEvent(event); console.log(event.keyCode); })
经常使用非字符键的键码:
左箭头:37;上箭头:38;右箭头:39;下箭头40;上翻页:33;下翻页:34;退出(ESC):27。
不管keydown或者keyup事件都会存在一些特殊状况。在Firefox和Opera中,按分号键时keyCode为59,也就是ASCII中分号的编码;但在IE,Safari,Chrome中返回186,即键盘中按键的键码。
IE九、Firefox、Chrome和Safari的event对象都支持一个charCode属性,这个属性只有在发生keypress事件时才包含值,并且这个值是按下的那个键所代码字符的ASCII编码。IE8及以前的版本和Opera则是在keyCode中保存字符的ASCII编码。下面以跨浏览器取得字符编码,放在EventUtil对象中:
getCharCode:function(event){ if(typeof event.charCode == 'number'){ return event.charCode; }else{ return event.keyCode; } },
使用方式:
var textbox = document.getElementById('myText'); EventUtil.addHandler(textbox,'keypress',function(event){ event = EventUtil.getEvent(event); console.log(EventUtil.getCharCode(event)); })
在取得字符编码后,就可使用String.fromCharCode()将其转化为实际的字符。以下:
var textbox = document.getElementById('myText'); EventUtil.addHandler(textbox,'keypress',function(event){ event = EventUtil.getEvent(event); var charCode = EventUtil.getCharCode(event); var text = String.fromCharCode(charCode); })
DOM3级事件中的键盘事件,再也不包含charCode属性,而是包含两个新属性:key和char,因为这个两个属性各浏览器支持程度不同,不推荐使用。
DOM3级事件在添加一个location属性,也不推荐使用。
“DOM3级事件”规范中引入了一个新事件,textInput。根据规范,当用户在可编辑区域中输入字符时,就会触发这个事件。keypress和textInput的区别:
因为textInput事件主要考虑的是字符,所以它的event对象中还包含一个data属性。这个属性的值就是用户输入的字符(而非字符编码)。
var textbox = document.getElementById('myText'); EventUtil.addHandler(textbox,'textInput',function(event){ event = EventUtil.getEvent(event); console.log(event.data) })
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'; }) })
之因此有发生在window对象上的beforeunload事件,是为了让开发人员在页面卸载前阻止这一操做。能够经过这个事件来取消卸载并继续使用原有页面。
EventUtil.addHandler(window,'beforeunload',function(event){ event = EventUtil.getEvent(event); var message = "你肯定要离开这个页面吗?"; event.returnValue = message; return message; })
window的load事件会在页面中的一切都加载完毕时触发,但这个过程可能会由于要加载的外部资源过多而颇费周折。而DOMContentLoaded事件则在造成完整的DOM树以后就会触发,不理会图像、js文件、css文件或其它资源是否已经下载完毕。与load事件不一样,DOMContentLoaded支持在页面下载的早期添加事件处理程序,这也就意味着用户可以尽早地与页面进行交互。
要处理DOMContentLoaded事件,能够为document或window添加相应的事件处理程序(尽管这个事件会冒泡到window,但它的目标其实是document)。
EventUtil.addHandler(document,"DOMContentLoaded",function(event){ alert('content loaded'); })
IE9以及其它浏览器支持该事件,对于不支持DOMContentLoaded的浏览器,建议在页面加载期间设置一个时间为0毫秒的超时调用,以下:
setTimeout(function(){ //在此添加事件处理程序 },0)
往返缓存(back-forward cache,或bfcache),用户使用浏览器的“后退”、“前进”按钮加快页面的转换速度。将整个页面保存在内存里。
(function(){ var showCount = 0; EventUtil.addHandler(window,'load',function(){ alert('loaded fired') }); EventUtil.addHandler(window,"pageshow",function(event){ showCount++; alert("Show has been fired" + showCount + "times."); }); })()
触摸事件以下:
上面这几个事件都会冒泡,也均可以取消。它们是以兼容DOM的方式实现的。所以,每一个触摸事件的event对象都提供了在鼠标事件中常见的属性:bubbles 、cancelabel 、view 、clientX 、clientY 、screenX、 screenY 、detail 、altKey、 ctrlKey、 shiftKey 和metaKey。
除了常见的DOM属性外,触摸事件还包括下列三个用于跟踪触摸的属性。
每一个touch对象包含下列属性:
以下面例子:
function handlerTouchEvent(event){ //只跟踪一次触摸 if(event.touches.length == 1){ var output = document.getElementById('output'); switch(event.type){ case "touchstart": output.innerHTML = "Touch started(" + event.touches[0].clientX + "," + event.touches[0].clientY + ")"; break; case "touchend": output.innerHTML += "<br/>Touch ended(" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"; break; case "touchmove": event.preventDefault(); //阻止滚动 output.innerHTML += "<br/>Touch moved(" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"; break; } } } EventUtil.addHandler(document,"touchstart",handlerTouchEvent); EventUtil.addHandler(document,"touchend",handlerTouchEvent); EventUtil.addHandler(document,"touchmove",handlerTouchEvent);
注意,在touchend事件发生时,touches集合中就没有任何的touch对象了,由于不存在活动的触摸操做;此时就必须转而使用changedTouches集合。
在触摸屏幕上的元素时,这些事件(包括鼠标事件)发生的顺序以下:
目前只有IOS版的Safari支持多点触摸。
在JS中,添加到页面上的事件处理程序数量将直接关系到页面的总体运行性能。致使这一问题的缘由是多方面的。首先,每一个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定全部事件处理程序而致使的DOM访问次数,会延迟整个页面的交互就绪时间。事实上,从如何利用好事件处理程序的角度出发,仍是有一些办法可以提高性能的。
事件委托利用事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件。例如,click事件会冒泡到document层次。也就是说,咱们能够为整个页面添加一个onclick事件处理程序,而没必要给每一个可单击的元素分别添加事件处理程序。
以下代码,点击3个li分别执行不一样的操做:
html代码:
<ul id="myLinks"> <li id="goSomewhere">Go somewhere</li> <li id="doSomething">Do something</li> <li id="sayHi">Say hi</li> </ul>
JS代码:
var list = document.getElementById('myLinks'); EventUtil.addHandler(list,'click',function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id){ case "goSomewhere": document.title = "I changed the document's title"; break; case "doSomething": window.location.href = "https://www.baidu.com"; break; case "sayHi": alert('hi'); break; } })
html:
<div id="myDiv"> <input type="button" value="click me" id="myBtn"/> </div>
JS:
var btn = document.getElementById('myBtn'); btn.onclick = function(){ //先执行某些操做 btn.onclick = null; document.getElementById('myDiv').innerHTML = 'Processing...'; }
咱们在设置<div>的innerHTML属性以前,先移除了按钮的事件处理程序。这样就确保了内存能够被再次利用。
注意,在事件处理程序中删除按钮也能阻止事件冒泡。目标元素在文档中是事件冒泡的前提。
能够在document对象上使用createEvent()方法建立event对象。这个方法接收一个参数,表示要建立的事件类型的字符串。在DOM2级中,全部这些字符串都使用英文复数形式,而在DOM3级中都变成了单数。这个字符串能够是下列字符串之一:
在建立了event对象后,还须要使用与事件有关的信息对其进行初始化。每种类型的event对象都有一个特殊的方法,为它传入适当的数据就能够初始化该event对象。不一样类型的方法的名字也不相同,这个取决于createEvent()中使用的参数。
事件模拟的最后一步就是触发事件,使用dispatchEvent()方法。调用dispatchEvent()方法须要传入一个参数,即表示要触发事件的event对象。
为createEvent()方法传入“MouseEvents”,返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息。这个方法接收15个参数,分别与鼠标事件中的每一个典型的属性一一对应,这些参数以下:
默认对按钮的单击事件:
var btn = document.getElementById('myBtn'); //建立事件对象 var event = document.createEvent('MouseEvents'); //初始化对象 event.initMouseEvent('click',true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null); //触发事件 btn.dispatchEvent(event);
DOM3级规定,调用createEvent()并传入“KeyboardEvent”就能够建立一个键盘事件。返回的事件对象包含一个initKeyboardEvent()方法,这个方法接收以下参数:
因为DOM3级不提倡使用keypress事件,所以只能利用这种技术来模拟keydown和keyup事件。
var textbox = document.getElementById('myTextbox'), event; //以DOM3级方式建立事件对象 if(document.implementation.hasFeature("KeyboardEvents","3.0")){ event = document.createEvent('KeyboardEvent'); //初始化对象 event.initKeyboardEvent('keydown',true,true,document.defaultView,'a',0,'shift',0); } //触发事件 textbox.dispatchEvent(event);
通用事件模拟:
var textbox = document.getElementById('myTextbox'); var event = document.createEvent('Events'); //初始化对象 event.initEvent('keydown',true,true); event.view = document.defaultView; event.altKey = false; event.ctrlKey = false; event.shiftKey = false; event.metaKey = false; event.keyCode = 65; event.charCode = 65; //触发事件 textbox.dispatchEvent(event);
当须要模拟变更事件时,可使用createEvent(‘MutationEvents’)建立一个包含initMutationEvent()方法的变更事件对象。这个方法接收的参数包括:type、bubbles、cancelable、relatedNode、preValue、newValue、atrrName和attrChange。以下例子:
var event = document.createEvent('MutationEvents'); //初始化对象 event.initMutationEvent('DOMNodeInserted',true,fase,someNode,"","","",0); //触发事件 target.dispatchEvent(event);
模拟HTML事件:
var event = document.createEvent('HTMLEvents'); //初始化对象 event.initEvent('focus',true,true); //触发事件 target.dispatchEvent(event);
DOM3级定义了“自定义事件”。自定义事件不是由DOM原生触发的,它的目的是让开发人员建立本身的事件。建立自定义事件,调用createEvent('CustomEvent'),返回的对象有一个名为initCustomEvent()方法,接收4个参数:
var div = document.getElementById('myDiv'), event; EventUtil.addHandler(div,'myevent',function(event){ console.log("DIV:" + event.detail); //DIV:Hello world! }) EventUtil.addHandler(document,'myevent',function(event){ console.log("DOCUMENT:" + event.detail); //DOCUMENT:Hello world! }) if(document.implementation.hasFeature("CustomEvents","3.0")){ event = document.createEvent('CustomEvent'); event.initCustomEvent("myevent",true,false,"Hello world!"); div.dispatchEvent(event); }
支持自定义DOM事件的浏览器有IE9+和Firefox6+。
在IE8及以前的版本中模拟事件跟DOM中的模拟事件的思路相似。只是每一个步骤采用了不同的方式。
调用document.createEventObject()方法能够在IE中建立event对象,这个方法不接受参数,结果会返回一个通用的event对象。而后,手工给这个对象添加必要的信息。最后一步在目标上调用fireEvent()方法,这个方法接收两个参数:事件处理程序的名称和event对象。在调用fireEvent()方法时,会自动为event对象添加srcElement和type属性。
下面例子模拟了在一个按钮上触发click事件的过程:
var btn = document.getElementById('myBtn'); //建立对象 var event = document.createEventObject(); //初始化事件对象 event.screenX = 100; event.screenY = 0; event.clientX = 0; event.clientY = 0; event.altKey = false; event.ctrlKey = false; event.shiftKey = false; event.button = 0; btn.fireEvent('onclick',event);
注:这里能够为对象添加任意属性,不会有任何限制。
附上上面完整的EventUtil对象:
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; } }, //获得event对象 getEvent:function(event){ return event ? event : window.event; }, //获得事件目标 getTarget:function(event){ return event.target || event.srcElement; }, //获得相关元素信息 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; } }, //取消默认行为 preventDefault:function(event){ if(event.preventDefault){ event.preventDefault(); }else{ event.returnValue = false; } }, //获得button属性 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; } } }, //取得鼠标滚轮增量值(delta) getWheelDelta:function(event){ if(event.wheelDelta){ return event.wheelDelta; }else{ return -event.detail * 40; } }, //字符编码 getCharCode:function(event){ if(typeof event.charCode == 'number'){ return event.charCode; }else{ return event.keyCode; } }, //移除事件处理程序 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; } }, //阻止事件捕获或冒泡 stopPropagation:function(event){ if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true; } } };