事件流
事件冒泡
从当前事件发生位置开始向父节点遍历,直至到达document对象.
事件捕获
从document对象开始,向发生事件位置继续遍历.
DOM事件流
DOM2 Event 规范事件流分为3个阶段:事件捕获,到达目标,事件冒泡.
事件处理程序
为响应事件而调用的函数被称为事件处理程序.
HTML事件处理程序
<button onclick='console.log('点击事件发生')'>点击</button>
作为事件处理程序执行的代码可以访问全局作用域中的一切.
通过这种事件处理函数,首先我们会创建一个函数来封装属性的值,这个函数有一个特殊的局部变量event,其中保存的就是event对象.
处理程序中的参数可以是document,自身的属性,也有可能包含父节点的属性.
HTML事件处理程序的问题
时间问题:要在事件处理程序中的js代码被解析完成后,才能进行事件触发,如果在事件触发时,事件处理程序中的代码还无法执行,将会报错.
对事件处理程序作用域链的扩展在不同浏览器中可能导致不同的结果.
DOM0事件处理程序
let a=document.getElementByTagname('button');
a.onclick=function(){
console.log('点击事件发生')
}
在事件处理程序中通过this可以获取元素的任何属性和方法.
以这种方式添加事件处理程序,是注册在事件流的冒泡阶段.
把事件处理程序设置为null,就不会执行任何操作.
DOM2事件处理程序
DOM2 Event为事件处理程序定义了两个方法:
addEventListener() 向节点中添加事件处理程序
removeEventListener(),移除节点的某个事件处理程序
他们接受3个参数,事件名,事件处理函数,和一个布尔值(代表事件处理程序是添加到事件流的捕获阶段true还是冒泡阶段false)
a.addEventListener('click',function(){console.log('点击事件触发')},false)
a.removeEventListener('click',function(){console.log('点击事件触发')},false)
捕获用于拦截,冒泡用于处理.
addEventListener()添加的匿名函数无法移除.
跨浏览器的事件处理程序
var EventUtil = {
//添加事件处理
//参数为:指定节点,事件类型,事件处理函数
addEvent : function (element,type,func){
if(element.addEventListener)
{
element.addEventListener(type,func,false);
}else{
element['on'+type]=func;
}
},
//移除事件处理
//参数为:指定节点,事件类型,事件处理函数
removeEvent: function(element,type,func){
if(element.removeEventListener){
element.removeEventListener(type,func,false);
}else{
element['on'+type]=null;
}
},
}
数据对象
在DOM中,发生事件时,所有相关信息都会被收集并储存在event对象中.
DOM事件对象
event对象是传给事件处理程序的唯一参数
事件对象的属性和方法
属性/方法 | 类型 | 读/写 | 说明 |
bubbles | Boolean | 只读 | 表明事件是否冒泡 |
cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为 |
currentTarget | Element | 只读 | 指向事件处理程序所处理事件的触发元素 |
defaultPrevent | Boolean | 只读 | 为true表示已经调用了preventDefault()方法 |
detail | Integer | 只读 | 与事件相关的细节信息 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段 1表示捕获阶段 2表示处于目标阶段 3表示冒泡阶段 |
preventDefault() | Function | 只读 | 取消事件的默认行为,当cancelable为 true 时才能使用该方法 |
stopImmediatePropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡,针对同一事件的多个事件处理程序(即事件终止于当前事件处理程序不会继续,就算是同一个元素的同一个事件的不同事件处理程序也不会触发) |
stopPropagation() | Function | 只读 | 取消事件的下一阶段的事件处理程序,但是当前阶段的不同事件处理程序都会触发 |
target | Element | 只读 | 事件的目标元素 |
trusted | Boolean | 只读 | true表示事件是浏览器生成,false表示事件为JS创建 |
type | String | 只读 | 事件触发的类型 |
view | AbstractView | 只读 | 与事件关联的抽象视图,等同于发生事件的window对象 |
在事件处理程序的内部,this 始终等于 currentTarget 的值
而target只包含事件的实际目标
也就是说this和current指向的是注册当前事件的元素,而target是当前事件的目标元素
这两者可能有一样的值,而在事件委托时我们将事件注册到目标元素的父元素上,这两者就不相同了
如果希望阻止事件的默认行为,可以使用preventDefault() 方法
不过只有cancelable 属性为true 的事件才能阻止默认行为
而关于stopImmediatePropagation()和stopPropagation()的区别在于,前者让事件流停止在当前事件处理程序,而后者是让事件流停在当前事件流阶段
此外,只有在事件处理程序执行期间event 对象才会存在,一旦执行完毕 event 对象就会销毁
事件类型
用户界面事件 当用户与页面元素交互时触发
焦点事件 当元素失去或获得焦点时触发
鼠标事件 当用户通过鼠标在页面上执行操作时触发
滚轮事件 当使用鼠标滚轮或类似设备时触发
文本事件 当在文档中输入文本时触发
键盘事件 当用户通过键盘在页面执行操作时触发
合成事件 当为IME(输入法编辑器)输入字符时触发
变动事件 当底层DOM结构发生变化时触发
除了这几类事件之外,HTML5也定义了一组事件,有的浏览器还会再BOM和DOM中实现其它专有事件
这些专有事件一般都是根据开发人员的需求定制的,没有规范,因此不同浏览器的实现可能不同.
用户界面事件
load:当页面完全加载后在window对象上触发,图像加载完毕在 img 标签触发,嵌入内容加载完毕在 object 元素触发
unload:页面完全卸载后在 window 对象触发,框架卸载完成在框架集上触发,嵌入内容卸载完成在 object 元素触发
abort:在用户停止下载时,若嵌入内容没有加载完则在 object 元素触发
error:JS发生错误时在 window 上触发,无法加载图像在 img 元素触发 ,无法加载嵌入内容在 object 内容触发
select:用户选中文本框(input、texterea)的一个或多个字符时触发
resize:当窗口或框架的大小发生变化时触发
scroll:用户滚动带滚动条的内容时触发
load事件
load可以给img标签元素设置事件处理程序.加载图片并不一定要把img标签元素添加到文档,只要给它设置了src属性就会立即开始下载.
同时可以给script标签元素加载完成后触发load事件,从而可以动态检测.与img标签元素不同,要下载js文件必须同时指定src属性并把script元素添加到文档中.
unload事件
unload事件会在文档卸载完成后触发,unload事件一般是从一个页面导航到另一个页面时触发.最常用于清理引用,以避免内存泄漏.
resize事件
当页面被缩放到新高度或宽度时触发.
是发生在window上的事件
scroll事件
虽然scroll 事件在window 上触发,但是其实际表示的时页面中相应元素的变化
在混杂模式下可以通过body 的 scrollLeft、scrollTop 来监测
而在标准模式下则会通过html 元素来反映这一变化
window.onscroll = function(event){
if(document.compatMode ==="CSS1Compat"){
alert(document.documentElement.scrollTop);
} else {
alert(document.body.scrollTop);
}
}
与resize事件类似,scroll 事件也会在滚动期间重复触发,所以需要尽量保持事件处理程序代码的简单
焦点事件
焦点事件会在页面元素获得或者失去焦点时触发,利用焦点事件和document.hasFocus() 方法配合使用
以及document.activeElement 属性配合可以知晓用户在页面上的行踪
焦点事件有以下6种:
blur:在元素失去焦点时触发,该事件不会冒泡
focus:元素获得焦点触发,该事件不会冒泡
focusin:与focus等价支持冒泡
focusout:与blur等价支持冒泡
鼠标与滚轮事件
DOM3级事件中定义了9个鼠标事件:
click:在用户单击主鼠标按钮(一般为鼠标左键)或者按下回车时触发,这一点对确保易访问性十分重要,这意味着 onclick 事件处理程序既可以通过键盘执行也可以通过鼠标执行
dbclick:在用户双击主鼠标按钮时触发(鼠标左键)。从技术上说该事件并不是DOM2级规范所支持的,但是由于浏览器对其的广泛实现在DOM3级规范中将其纳入标准
mousedown:用户按下任意鼠标按钮触发,不能通过键盘触发
mouseenter:鼠标光标从元素外部移入元素内部时触发,该事件不冒泡,并且在光标从元素移入元素子元素时不会触发
mouseleave:光标从元素上方移出元素范围时触发,该事件不冒泡,移入后代元素不会触发
mousemove:鼠标指针在元素内部移动时重复触发,不能通过键盘触发该事件
mouseout:鼠标位于元素上方,此时将鼠标移至另一个元素触发,其子元素也会触发,不能通过键盘触发该事件
mouseover:鼠标位于元素外部,首次移入元素范围内触发,其子元素也会触发,不能通过键盘触发该事件
mouseup:用户释放鼠标按钮时触发,不能通过键盘触发
需要注意的是,页面上所有元素都支持鼠标事件。除了mouseenter和mouseleave事件,其余的鼠标事件都会冒泡,也可以被取消,取消鼠标事件会影响浏览器的默认行为
只有在同一个元素上相继触发mousedown 和 mouseup 事件才会触发click 事件,若两者中有一者被取消,那么就不会触发click事件。
客户端坐标
clientX
clientY
鼠标事件都是在浏览器视口的特定位置发生的
该位置信息保存在鼠标事件对象的clientX 和 clientY 属性中
他们的值分别表示事件触发时鼠标指针在浏览器窗口中的水平坐标和垂直坐标
注意该坐标不包括浏览的的工具栏等,也就是说该坐标是浏览器可视窗口内部的坐标(相对于浏览器窗口而非文档,也就是并不表示该元素在页面中的位置)
var div = document.getElementByTagName("div")[0];
div.onclick = function(event){
alert("Client coordinates:"+ event.clientX +","+event.clientY);
}
页面坐标
pageX
pageY
而页面坐标的信息则保存在事件对象的pageX 和 pageY 属性中
该属性可以告知我们是事件是在页面中的什么位置发生的
屏幕坐标
鼠标事件发生时,不仅会有相对于浏览器窗口的位置,还有一个相对于整个电脑屏幕的位置
而相对于电脑屏幕的位置信息保存在事件对象的screenX 和 screenY 属性上
修饰符
虽然鼠标事件主要是由鼠标来触发的,但在按下鼠标时键盘上某些键的状态也可以影响到索要采取的操作
这些键为:
shift
ctrl
alt
Meta(windows是win键苹果是CMD键)
他们经常被用于修改鼠标事件的行为
DOM为此规定了四个属性,用于表示这些修改键的状态
shiftKey
ctrlKey
altKey
metaKey
这四个属性都保存着布尔值,以表示鼠标事件触发时这些修改键的状态(true 表示按下 false 表示没有按下)
通过检测这些事件属性,我们可以更加灵活地使用鼠标事件
相关元素
relatedTarget
在发生mouseover 和 mouseout 事件时,除了触发元素外还会涉及更多元素
因为这两个事件都涉及从一个元素的边界之内移入另一个元素的边界之内
对mouseover 事件而言事件的主目标是 获得光标的元素 而相关元素就是那个失去光标的元素
类似的,对于mouseout 事件而言 失去光标的是主目标,而相关元素则是获得光标的元素
DOM 通过 event 对象的 relatedTarget 属性提供相关元素的信息
该属性只对mouseout 和 mouseover 事件存在,其它事件该属性的值为 null
鼠标按键
只有在主鼠标按钮被单击的时候才会触发click
因此检测按钮的信息不是必要的
但是对于mousedown 和 mouseup 事件来说用户点击的是鼠标的哪一个按钮是一个比较重要的信息
所以在其event 对象存在一个 button 属性,表示按下或者释放的按钮
DOM中的 button 属性可能有以下3个值:
0 表示主鼠标按钮
1 表示中间鼠标按钮
2 表示次鼠标按钮
在常规设置中,主鼠标按钮一般是指鼠标左键中鼠标按钮指鼠标滚轮,次鼠标按钮则指的是鼠标右键
额外事件消息
detail
DOM2级事件“规范在 event 对象中还提供了 detail 属性,用于给出有关事件的更多信息
对于鼠标事件来说,detail 包含了一个数值,表示在给定位置上发生了多少次单击
在同一个元素上相继发生一次mousedown 和 mouseup 算作发生一次单击
detail属性从1开始计数,每次单击发生后都会递增,若在mousedown和mouseup之间鼠标移动了位置则 detail 会被重置为0
mouseWheel事件
wheelDelta
鼠标滚轮事件由IE6.0首先实现,此后其它浏览器也相继实现了该事件
与mousewheel 事件对应的 event 对象除了包含鼠标事件的所有标准信息之外还包含一个特殊的 wheelDelta 属性
当用户向前(远离自己)的方向滚动滚轮时该值是120的倍数
当用户向后(靠近自己)的方向滚动滚轮时该值是-120的倍数
触摸设备
IOS和Android设备实现非常特别,因为这些设备没有鼠标
需要注意以下几点:
1.不支持dbclick,双击会放大画面且无法修改该行为
2.轻击可单击元素会触发mousemove事件,该操作导致内容变化则不再发生其它事件,若没有变化则会依次发生 mousedown mouseup click 事件
3.mousemove 会触发 mouseover 和 mouseout
4.两个手指放在屏幕上滑动页面会mousewheel事件
无障碍问题
若web应用需要确保残疾人或者使用屏幕阅读器的人能够访问,使用鼠标事件就需要特别小心
因此不建议使用click 之外的其他鼠标事件来展示功能或者引发代码执行
需注意以下三点:
不要为了加快速度而使用mousedown事件代替 click 事件,因为在屏幕阅读器无法触发该事件
不要使用mouseover 向用户展示新选项,原因同上,无法触发
不要使用dbclick 执行重要操作,因为键盘无法触发该事件
以上可以提升web应用对残疾人的易访问性
键盘与输入事件
有三个键盘事件:
keydown:当用户按下键盘上的任意键时触发,按住不放会重复触发
keypress:用户按下键盘产生字符键时触发,按住不放重复触发(按下ESC键也会触发)
keyup:用户释放按键触发
所有元素都支持上述三个事件,但只有用户通过文本文本框输入文本时比较常用
文本事件只有一个就是textInput 事件
该事件是对keypress 的补充
其目的在于将文本显示给用户之前实现拦截,在文本插入文本框之前都会触发该事件
以上事件中keydown 和 keypress 事件会在文本框发生变化之前触发
keyup则会在文本框变化之后触发,若按住键不放则会持续触发 keypress 和 keydown,直到用户释放按键时才触发 keyup
键码
在发生keydown 和 keyup 事件时,event对象的 keyCode 属性中会包含一个代码,与键盘上的键对应,即键码
字符编码
发生keypress 事件意味着按下的键会影响到屏幕中的文本信息
在所有浏览器,按下能够插入或删除字符的键都会触发keypress 事件
主流浏览器keypress 事件的 event 对象都支持一个 charCode 属性
该属性只有发生keypress 事件的时候才有值,这个值通常是按下键的 ASSCII 编码
此时的keyCode 通常等于零 或者按键的键码
DOM3级变化
我们知道DOM2级事件主要是关于 addEventListener 等规范,DOM3级事件主要规范的则是键盘事件
尽管所有浏览器都实现了某种形式的键盘事件,DOM3级事件还是做出了一些改变:
DOM3级事件中的键盘事件将不再包含 charCode 属性,取而代之的时两个新属性:key 和 char
key:用于取代 keyCOde 而新增,其值为一个字符串。按下字符键时key的值为按键对应的字符,按下非字符按键其值则是键对应的名如(shift)
char:对于字符按键行为与 key 相同,非字符按键其值为null
此外DOM3级事件还添加了名为 location 的属性,是一个数值,表示按下了什么位置上的键
0:默认键盘
1:左侧
2:右侧
3:小键盘
4:移动设备键盘
5:手柄
最后还给event 对象添加了 getModifierState()方法
该方法接收一个参数,即shift 、meta等修改键名的字符串,表示要检测的修改键,若调用该方法时修改键处于被按下的状态则返回 true 否则返回 false
textInput事件
该事件是DOM3级规范引入的新事件,根据规范,当用户在可编辑区域输入字符时则会触发该事件
该事件与keyPress 事件的区别在于,textInput 只有可编辑区域才能触发,而keypress 任何可以获得焦点的元素都能触发
由于textinput 事件主要考虑的是字符,因此他的event对象中还包含一个属性 data
该属性的值为用户输入的字符
此外 event 对象上还有一个属性叫做 inputMethod,表示文本输入到文本框中的方式
0:浏览器不确定的方式
1:键盘输入
2:粘贴
3:拖放
4:使用IME(输入法编辑器)输入
5:表单选择输入
6:手写输入
7:语音输入
8:组合输入
9:脚本输入
复合事件
复合事件是DOM3 中新增的一类事件,用于处理 IME 的输入序列
IME(输入法编辑器)通常用于输入物理键盘上找不到的字符,而这种输入方式通常需要同时按住多个键,但最终只输入一个字符
复合事件就是针对检测和处理这种输入而设计的
复合事件有以下三种:
compositionstart:在IME的文本复合系统打开时触发
compositionupdate:向输入字段中插入新字符时触发
compositionend:在IME的文本复合系统关闭时触发,表示返回键盘的正常输入状态
在大体上复合事件与文本事件在很多方面相似
在其触发时,目标是接收文本的输入字段,但它比文本事件对象多一个data属性
data属性的值,为以下值中的一个:
在触发compositionstart 时,值为正在编辑的文本
在触发compositionupdate 时,值为正在插入的文本
在触发compositionend 时,值为此次操作已经插入的字符
否则为空字符串
复合事件可冒泡
HTML5事件
contextmenu事件
windows95 在PC中引入了上下文菜单的概念,即通过鼠标右键可以调出上下文菜单
在该概念提出后不久,该概念也被引入了web 领域
在web中实现该概念,开发人员面临的主要问题是:1.如何确定上下文菜单以何种方式打开(windows中鼠标右键,Mac中 ctrl + 单击) 2.如何屏蔽默认的上下文菜单的行为
为解决以上问题,出现了contextmenu 这一事件,该事件触发时表示正在访问上下文菜单
通过这一事件,开发人员可以方便地提供自定义菜单
由于该事件是冒泡的,所以我们只需要为document 提供事件处理程序,就可以将默认的菜单行为替代为我们自定义的菜单
需要注意的是该事件属于鼠标事件,所以在其事件对象上会提供光标位置有关的属性
通过这一属性我们就可以将我们的自定义菜单放到用户触发该事件时的位置,也就像我们平常使用的时候那样菜单出现在我们光标的位置
不过在使用时我们还需要阻止浏览器的默认菜单行为
在兼容DOM的浏览器中,我们可以使用 event.preventDefalut() 方法来取消浏览器的默认菜单行为
在不兼容DOM的浏览器中(如 IE低版本) 则需要将 event.returnValue 设置为false 来阻止默认行为
如下代码所示:
<ul style = "position:absolute;visibility:hidden;background-color:sliver" id="menu">
<li><a href="www.baidu.com">百度</a><li>
<li><a href="www.google.com">谷歌</a><li><ul>
上方代码在文档中创建了一个隐藏的菜单,该菜单由两个网址连接组成
这样我们就准备好了自定义菜单,下面我们则需要把浏览器默认的上下文菜单替换为我们的自定义菜单
window.onload = function(event){//当文档加载完成我们就需要替换掉默认的菜单
var menu = document.getElementById("menu");
document.addEventLinsener("contentmenu",function(events){
events.preventDefault();//阻止默认行为
menu.style.left = events.clientX + "px";
menu.sytle.top = event.clientY + "px";
menu.style.visibility = "visible"
},false);
document.onlick = function(){
menu.style.visibility = "hidden";
}// 点击页面隐藏菜单
}
beforeunload事件
beforeunload事件用于让开发人员能够在浏览器卸载页面之前,阻止这一行为
这一事件主要用于,弹出一个询问是否离开当前页面的弹窗,通过这一事件将控制权交给用户,防止因用户的误操作带来的不便
那么如何在浏览器中实现呢?
如下方代码所示:
window.addEventListener("beforeunload",function(event){
event.preventDefault();
event.returnValue = '';//chrome浏览器需要设置
},false);
处于安全性的考虑,现代的主流浏览器并不支持由我们来提供用户的提示信息
也就是我们没有必要设置returnValue,但是chrome 浏览器需要设置以下该值(什么值都行只要不是undefined)才能正常工作
DOMContentLoaded事件
我们知道,window的load事件会在页面中的所有内容加载完毕后触发
但是往往我们在DOM结构建立完成的时候就需要对页面内容进行操作了,如果加载css等资源的时间过长,那么用户如果在这加载期间进行操作,就会给人没有响应的感受,脾气不好的用户很可能就会关掉网页,这当然不是我们想要看到的
对于这样的一种需求,DOMContetLoaded 事件应运而生
该事件在当前文档形成完整的DOM树之后就会触发
通过该事件我们可以让用户更早地和页面进行交互
要处理这一事件我们可以对Document、window 绑定事件处理程序
虽然该事件会冒泡到window,但是该事件的目标是document
而这一事件的event 事件对象并不会提供额外的信息
而对于不支持该事件的浏览器我们可以使用以下代码来代替:
setTimeout(function(){
//在此添加事件处理程序
},0);
需要注意的是,这样的写法并不一定代表这样添加的事件处理程序一定会早于load事件触发
readystatechange事件
IE为DOM中的某些部分提供,readystatechange 事件
该事件用于提供文档或元素加载状态的有关信息
该事件的事件对象有一个readyState 属性
该属性的值为下面五个值中的一个:
uninitialized:尚未初始化
loading:正在加载数据
loaded:对象加载数据完成
interactive:可以操作,尚未完全加载
complete:对象已经加载完毕
需要注意的是并非所有的对象都会经历以上的五个状态,通常情况下该事件触发次数会少于四次
对于document而言,值为“interactive”的阶段与 DOMContentLoaded 事件的阶段大致相同
该事件与DOMContentLoaded 事件最大的区别在于,无法判断该事件与 load 事件的先后顺序
也就是这一事件并不一定会早于load事件触发
document.attachEvent("onreadystatechange",function(event){
if(document.readyState =="intertactive" || document.readyState =="complete"){
document.detachEvent("onreadystatechange",arguments.callee,false);//解除事件处理程序
//需要进行的页面操作 }
},false)
需要注意的是:上方的代码可以近似地模拟DOMContentLoaded事件,但是其本质上仍是不同的,在不同的页面中load事件与readystatechange事件并不能保证以相同的顺序触发
pageshow与pagehide事件
在Firefox与Opera浏览器中有一个特性,叫“往返缓存”,用于当用户使用“前进”、“后退”按钮时加快页面的转换速度
本质上该特性相当于将当前整个页面存在内存中,所以当我们在上述浏览器使用前进、后退时并不会触发load事件
为此Firefox提供了一些新的事件
其中 pageshow 事件会在页面显示时触发,无论页面是否来自“往返缓存”
该事件会在load事件之后触发
虽然该事件的目标是document 但是必须将该事件的事件处理程序添加到 window 才能正常生效
该事件的事件对象有一个persisted 的布尔值属性,如果页面来自 “往返缓存”则为 true 否则为 false
与pageshow 对应的是pagehide,该事件会在页面卸载时触发(早于unload事件)
对于这两个事件,只有IE9以下和Safari5以下的浏览器不支持
hashchange事件
hashchange事件应该说是这些事件中用途最广的了
前些时候,当前后分离是主流的web应用的开发模式的时候,hashchange事件可以说是功不可没
url中的hash值可以用来在不同页面之间传递信息,也就是前端路由的一种实现方式
现如今更多的时候hashchange事件用于单页应用
我们知道单页应用只有一个HTML页面,通过这一个页面来实现许多复杂的功能
通过hashchange 事件我们可以将单页应和多页应用的表现一致化
话说回来,该事件会在页面的url 中的 # 后面的部分(hash值)发生变化时触发
该事件的event 对象包含两个额外的属性:
oldURL:变化前的url
newURL:变化后的url
虽然大多数浏览器实现了hashchange事件
但是其中只有一部分支持,上面的额外属性,所以最好使用loaction 对象来获取当前的 url的参数列表
内存与性能
事件委托
对于页面中事件处理程序过多这一现象的解决方法就是采用事件委托
事件委托可以大幅降低页面中事件处理程序的数量
那么什么是事件委托呢?
事件委托就是利用事件冒泡,给整个页面添加一个事件处理程序就可以管理这一类的事件
以onclick 事件为例
一般情况下我们很有可能为页面上的所有可点击元素都添加他们的事件处理程序
但是这样一来页面上的事件处理程序的数量就可想而知了
但如果我们采用事件委托的方式,如下代码所示:
<ul id = "myLinks">
<li id="1">1</li>
<li id="2">2</li></ul>
对于上述的HTML结构,我们可以用下面的方式为其添加事件处理程序
var list = document.getElementById("myLinks");
list.onlick = function(event){
var target = event.target;
switch(target.id){
case "1":
alert("1");
break;
case "2":
alert("2");
break;
}
}
这样一来一个事件处理程序就可以完成原本两个或者更多事件处理程序的工作
如果条件允许的话,我们甚至可以直接为document添加一类的事件处理程序
然后在内部再对其进行细分,完成不同的操作
删除事件处理程序
JS中的垃圾回收机制我们之前提到过
除了对于循环引用可能造成内存泄漏之外
对于事件的处理不当也会造成内存泄漏,甚至更为严重
因为这一类的内存泄漏比循环引用更加不易察觉
由于事件处理程序会占用内存,所以一旦我们从页面中删除了事件处理程序所绑定的元素
而没有对已经绑定的事件处理程序解除引用,这样一来事件处理程序所占用的内存就得不到释放
随着泄漏的内存增加,页面性能自然就会逐渐降低
所以改善页面性能另一个重要的地方就在于要及时地移除事件处理程序
即当我们知道某个元素即将被移的时候,那么我们最好手动地移除事件处理程序
方法如下:
将onclick等事件属性设为 null
对于addEventListener 添加的事件 我们用对应的 removeEventListener移除即可
模拟事件
DOM事件模拟
首先我们知道事件处理程序和一般的函数的区别就在于,事件处理程序的函数在事件触发时会传入一个event 事件对象
那么要模拟事件首先就需要创建event 事件对象
创建事件对象我们可以调用document 上的 createEvent()方法创建 event 事件对象
该方法接收一个参数,用于表示要创建的事件的类型
在DOM2级规范中这些字符串都使用英文复数形式,而在DOM3中这些字符串都变成了单数形式
以DOM2级规范为例,该函数的参数可以是以下几个字符串之一:
UIEvents:一般的UI事件,鼠标事件和键盘事件都继承自UI事件
MouseEvents:一般的鼠标事件
MutationEvents:一般的DOM变动事件
HTMLEvents:一般的HTML事件(DOM3中没有对应的值,DOM3中HTML事件被拆分到了其它类别中)
在创建了event 对象之后,还需使用与事件有关的信息才能对其进行初始化,对于不同类型的 event 对象有不同的方法来对其进行初始化(取决于createEvent()方法传入的参数)
初始化完成后,我们就只需要在代码中我们希望的位置触发即可
触发时我们需要在DOM元素上调用 dispatchEvent()方法(该方法得到所有能够触发事件的元素支持),此时需要传入我们之前创建的 event 对象作为该方法的参数
这样我们模拟的事件就像官方的事件那样能够冒泡,并且触发相应的事件处理程序了
模拟鼠标事件
创建新的鼠标事件并为其指定必要信息就可以模拟鼠标事件
这里需要详细讲的是初始化事件对象
我们在得到createEvent 方法创建的 MouseEvent 事件对象后,该对象上有一个方法:
initMouseEvent()
该方法接收15个参数以此对应正常的浏览器事件中的鼠标事件的事件对象:
type(字符串):表示触发鼠标事件的具体类型,如“click”
bubbles(布尔值):表示该事件是否冒泡,为准确模拟鼠标事件应该为 true
cancelable(布尔值):表示该事件是否可以取消,为准确模拟应为 true
view(AbstractView):与事件关联的视图,应该设置为 document.defaultView
detail(整数):与事件有关的详细信息,此处应该设为0
screenX(整数):相对于屏幕的X坐标
screenY(整数):相对于屏幕的Y坐标
clientX(整数):相对于视口的X坐标
clientY(整数):相对于视口的Y坐标
ctrlKey(布尔值):表示是否按下ctrl,默认false
altKey(布尔值):表示是否按下alt,默认false
shiftKey(布尔值):表示是否按下shift键,默认false
metaKey(布尔值):表示是否按下meta键,默认false
button(整数):表示按下哪一个鼠标键
relatedTarget(元素):表示与当前事件关联的元素对象,该参数只在模拟mouseover或mouseout时使用
由于是在希望触发的元素上调用dispatchEvent(),所以 tartget 属性会自动传入
代码如下:
var btn = document.getElementById('myButton');
//创建事件对象var event = document.createEvent('MouseEvents');//DOM3不用s
//初始化事件对象
event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,false,false,false,false,0,null);
//触发事件
btn,dispatchEvent(event);
这样就完成了一个鼠标事件的模拟
模拟键盘事件
键盘事件在DOM3中正式纳入规范,所以这里只介绍DOM3中的模拟键盘事件
与模拟鼠标事件的区别在于
传入的事件类型字符串为:KeyboardEvent
事件包含的初始化方法为:initKeyEvent
initKeyEvent接收的参数如下:
type(字符串):表示要触发的事件类型,如“keydown”
bubbles(布尔值):表示事件是否应该冒泡,准确模拟应该为 true
cancelable(布尔值):表示事件是否可以取消,准确模拟应为 true
view(AbstractView):一般来说为 document.defaultView
key(字符串):按下键的键码
location(整数):表示按下了哪里的键,0默认主键盘,1左,2右,3数字键盘,4移动设备,5手柄
modifiers(字符串):空格分隔的修改键列表如”Shift Ctrl“
repeat(整数):在一行中按了这个键多少次
var event = document.createEvent('KeyboardEvent');
//初始化事件对象
event.initKeyboardEvent('keydown',true,true,document.defaultView,"a",0,"Shit ",0);
//触发事件
btn.dispatchEvent(event);
要注意的是火狐中模拟键盘事件和标准DOM中有所不同
事件类型参数:”KeyEvents“
初始化方法:initKeyEvent()
初始化方法的参数:从第五个开始不同
ctrlKey:表示是否按下 ctrl键
altKey:表示是否按下alt键
shiftKey:表示是否按下shift
metaKey:表示是否按下meta
keyCode(整数):被按下键的键码
charCode(整数):通过按键生成字符的ASCLL码
模拟变动事件
初始化参数为:
type
bubbles
cancelable
relatedNode:关联的DOM节点
preValue:变动前的值
newValue:变动后的值
attrName:特姓名
attrChange:特性变动
示例如下:
var event = document.createEvent("MutationEvents");
event.initMutationEvent("DOMNodeInserted",true,false,someNode,"","","",0);
target.dispatchEvent(event);
模拟HTML事件
以focus事件为例:
var event =document.createElement('HTMLEvents');
event.initEvent("focus",true,false);
target.dispatchEvent(event);
自定义事件
除了向上面那样模拟浏览器事件之外,我们也可以创建自己的自定义事件
与浏览器事件的区别在于,初始化事件不同
自定义事件的初始化方法为:initCustomEvent
接收以下参数:
type:触发事件的类型
bubbles:是否冒泡
cancelable:是否可以取消
detail(对象):可以是任意值
示例如下:
var event = document.createEvent("CustomEvent");
event.initCustomEvent("myEvent",true,false,"hello");
target.dispatchEvent(event);
像上面这样冒泡的自定义事件是可以其它元素指定针对该自定义事件的事件处理程序的
如:
document.addEventListener("myEvent",function(event){
console.log(event.detail);
},false);