IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。如下代码:
<body> <div id="myDiv">click me</div> </body>
如果你点击了上面的div元素,那么这个click事件会按照如下顺序传播:
所有现代浏览器都支持事件冒泡,但在具体实现上还是有一些差别。IE5.5以及更早的版本事件冒泡会跳过<html>元素(从<body>直接跳到document)。IE9及其它浏览器则将事件一直冒泡到window对象。
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。如上面的例子,单机div元素,那么就会按照以下顺序触发click事件:
由于老版本浏览器的不支持,因此很少有人使用事件捕获。
“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()添加的事件处理程序都会被添加到冒泡阶段。
btn.attachEvent(‘onclick‘,function(){ alert(this.value) //undefined })
结果为undefined的原因是,在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行。因此this等于window。在编写跨浏览器区别的时候,牢记这一区别非常重要。
attachEvent()方法也可以为一个元素添加多个事件处理程序,如下:
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(IE9、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,即键盘中按键的键码。
IE9、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);
注:这里可以为对象添加任意属性,不会有任何限制。
原文:https://www.cnblogs.com/moqiutao/p/10174200.html