@demonly
2017-02-26T15:31:42.000000Z
字数 12527
阅读 1378
JavaScript
事件由目标节点逐级传到根节点。
事件由根节点逐级传播到目标节点,一些老版本浏览器不支持事件捕获,因此推荐使用事件冒泡。
DOM2级事件规定事件流包含三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。即事件从根节点传播到目标节点然后再传回根节点。
某个元素的事件可以由元素的特性来指定,这个特性的值中不能够包括未经转义的尖括号或者双引号等。
<input type="button" onclick="try{showMessage();}catch(ex)">
通过这种方式指定事件处理程序的优点就是,这样会创建一个封装元素属性值的函数。这个函数中有一个局部变量 event,也就是事件对象。在这个函数的内部 this 值等于事件的目标元素。如果当前元素是一个表单输入元素,那么这个函数还可以访问到表单元素的入口。
缺点有两个,第一是如果在浏览器还没有加载到声明函数时触发了事件那么就会报错。这个问题的解决方法就是将函数放到 try catch 语句中,这样错误信息就会被拦截。第二是这种方式让 HTML 和 JavaScript 代码紧密耦合。
DOM0级的事件处理程序就是将一个函数赋值给一个对象的事件处理程序属性。函数中的 this 依然指向元素。
var btn = document.getElementById("myBtn")
btn.onclick = function (){
alert("Clicked")
}
需要删除 DOM0级方法指定的事件处理程序的话可以将这个属性设置为 null。
DOM2级指定了两个操作事件的方法,addEventListener()和 removeEventListener()。他们接受三个参数,第一个要处理的事件名称,第二个是处理事件的函数,第三个是一个布尔值。第三个参数为 true 表示在事件捕获阶段调用事件处理程序,为 false 表示在事件冒泡阶段调用事件处理程序。依然是不建议将事件处理程序添加到捕获阶段。
var btn = document.getElementById("myBtn")
用 addEventListener()添加的事件处理程序必须用 removeEventListener()来移除,而且两者的参数必须完全相同。因此 addEventListener()添加的匿名函数不能够被删除。
添加多个事件的话,事件会按照添加的顺序触发。
IE 中存在与 DOM 中相似的两个方法 attachEvent()方法和 detachEvent()方法。
与 DOM 的不同点
var EventUtil = {
addHandler : function(element, type, handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
//支持 DOM2级则使用 DOM2级事件处理程序
}else if(element.attachEvent){
element.attachEvent("on" + type, handler);
//兼容 IE 事件处理程序
}else{
elmenet["on" + type] = handler;
//都不支持则采用 DOM0级事件处理程序
}
],
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;
}
}
};
在触发某个 DOM 事件时会产生一个事件对象 event,这个对象中包含着与事件相关的所有信息。
兼容 DOM 的浏览器中,如果事件处理程序是匿名函数,那么第一个参数会作为事件对象传入函数,显式和隐式传入都可以。
btn.addEventListener('click',function(e){
alert(e);
},false) //[Object MouseEvent]
btn.addEventListener('click',function(){
alert(arguments[0]);
},false) //[Object MouseEvent]
对于需要移除的事件监听器,可以为匿名函数添加一个句柄。
var handler = function(e){
alert(e);
} //实质上依然是匿名函数
btn.addEventListener('click',handler,false)
event 中保存着 event 对象,event 对象包含了与事件有关的属性和方法。
属性或方法 | 说明 |
---|---|
currentTarget | 其事件处理程序当前正在处理事件的那个元素 |
detail | 与事件相关的细节信息 |
cancelable | 表明是否可以取消事件的默认行为 |
preventDefault() | 取消事件的默认行为,比如链接的导航行为 |
eventPhase | 调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段 |
stopImmediatePropagation() | 阻止事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用 |
stopPropagation() | 阻止事件的进一步捕获或冒泡,仅限于该事件 |
target | 事件的目标 |
trusted | 为 true 表示事件是浏览器生成的,false 表示是 JavaScript 创建的 |
type | 被触发的事件的类型 |
view | 与事件关联抽象视图,等同于发生时间的 window 对象 |
在事件处理程序内部,this 始终等于 currentTarget 的值,而 target 只包含事件的实际目标。当处于捕获和冒泡阶段时,target 指向被单击的对象,currentTarget 指向当前处理事件的元素。
<div>
<p></p>
</div>
比如在以上 div 元素中添加事件监听,然后点击 p 元素。这时的 target 指向被单击的对象,即 p 元素。而 currentTarget 和 this 指向正在处理事件的对象,即 div 元素。
IE 中访问 event 对象的方式取决于指定事件处理程序的方法。
属性 | 说明 |
---|---|
cancelBubble | 默认为 false,如果设置为 true 就可以取消事件冒泡 |
returnValue | 默认值为 true,如果设置为 false 可以取消事件的默认行为 |
srcElement | 事件的目标 |
type | 被触发的事件类型 |
IE 中事件处理程序的作用域是根据指定它的方式来确定的,因此 this 不一定会等于事件目标,应该更多地采用 srcElement 属性。
var eventUtil = {
getEvent : function(event){
return event ? event : window.event;
}; //获取 event 对象
getTarget : function(event){
return event.target || event.srcElement;
}; //获取 target 属性
preventDefault : function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
}; //阻止默认事件
stopPropagation : function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}; //阻止事件冒泡
};
function throttle(method,context){
clearTimeout(method.tId);
method.tId=setTimeout(function(){
method.call(context);
},500);
}
这个函数利用的原理是同一个任务在任务队列中只能存在一个,后加入的任务会被取消。这个函数将事件处理程序加入到任务队列中延迟5毫秒执行,在此期间这个事件不能够再次被加入任务队列。
除了 mouseenter 和 mouseout,所有鼠标事件都会冒泡。
clientX 和 clientY 属性记录了鼠标事件在发生时鼠标的位置,它的值表示鼠标在窗口中的位置,而不是在文档中的位置。
pageX 和 pageY 属性记录了鼠标事件发生时鼠标在文档中的位置。
screenX 和 screenY 属性记录了鼠标事件发生时鼠标在整个屏幕中的位置。
在鼠标事件触发时用户可能通过某些键来选取需要的操作。shiftKey、ctrlKey、altKey、metaKey 属性保存了这四个键的状态,被按下则为 true,没被按下则为 false。
在 mouseover 事件或者 mouseout 事件中,会有一个主元素还有一个相关元素,在 DOM 中 relativeTarget 属性会保存相关元素,而在 IE 中 toElement 元素会保存相关元素。
对于鼠标事件的 event 对象存在一个 button 属性,这个属性记录了鼠标上有哪些键被按下了。
DOM 中的 button 属性只有三个值,0表示主鼠标按钮,1表示中鼠标按钮,2表示次鼠标按钮。
IE 下的 button 属性如下
值 | 说明 |
---|---|
0 | 没有按下鼠标 |
1 | 按下了主鼠标按钮 |
2 | 按下了次鼠标按钮 |
3 | 同时按下了主次鼠标按钮 |
4 | 按下了中鼠标按钮 |
5 | 同时按下了主鼠标按钮和中鼠标按钮 |
6 | 同时按下了次鼠标按钮和中鼠标按钮 |
7 | 同时按下了三个鼠标按钮 |
event 对象的 detail 属性保存了与事件相关的更多信息。对于鼠标事件,detail 中保存了鼠标在给定位置单击的次数。如果鼠标移动了位置,那么 detail 计数会清零。
IE 下的鼠标事件拥有一系列属性
- altLeft:表示是否按下了 alt 键
- ctrlLeft:表示是否按下了 Ctrl 键
- shiftLeft:表示是否按下了 Shift 键
- offsetX:光标相对目标元素的 x 坐标
- offsetY:光标相对目标元素的 y 坐标
mousewheel 事件在鼠标滚轮滚动时触发。mousewheel 事件的 event 对象除了包含鼠标事件的标准信息以外还包括了 wheelDelta。当用户向上滚动滚轮时,mousewheel 为正,当用户向下滚动滚轮时,mousewheel 为负,它的值为120的倍数。用户滚动滚轮的速度越快,该数值的绝对值就越大。
Firefox 支持 DOMMouseScroll 事件,它的关于鼠标滚动的信息保存在 detail 中,它的正负性与 mousewheel 的 wheelDelta 相反,它的值为3的倍数。
Safari 中 wheelDelta 为360的倍数。
使用触摸板或者 magic mouse 的时候一次划动会触发多次 mousewheel 事件,数值递减,值为3的倍数。
当 keydown 和 keyup 事件触发时 event 对象中会有一个 keyCode 属性,它保存着一个值,与用户所按下的键相对应,对于字符键这个值与相应字符的 ASCII 码对应。
在一些浏览器中发生 keypress 时所按键的值保存在 charCode 属性中,而它们的 keyCode 此时可能为0也可能为 ASCII 码。因此需要检测是否支持 charCode 属性。
getCharCode:function(e){
if (typeof e.charCode == "number") {
return e.charCode;
} else {
return e.keyCode;
}
}
使用字符串的 String.fromCharCode()方法可以将字符编码转换为实际字符。
String.fromCharCode("100") //"d"
key 属性包含一个字符串,key 的值是按下的键的名字。
char 属性在按下字符键的时候包含所按下的字符,在按下非字符键时包含 null。
location 属性表示按下的是什么位置的键。0表示默认键盘,1表示左侧位置的键(如左 Shift),2表示右侧位置的键,3表示小键盘,4表示移动设备键盘,5表示手柄。
textInput 事件当用户在文本框中输入字符时触发,data 属性中包含着用户输入的字符。
inputMethod 属性包含着文本输入到文本框中的方式。
0:浏览器不确定是怎么输入的。
1:使用键盘输入的。
2:文本是粘贴进来的。
3:文本是拖放进来的。
4:文本是使用 IME(输入法编辑器)输入的。
5:文本是通过在表单中选择某一项输入的。
6:表示文本是手写输入的。
7:表示文本是语音输入的。
8:表示文本是通过几种方式组合输入的。
9:表示文本是通过脚本输入的。
进过测试,选择浏览器的输入历史记录不会触发 textInput 事件。
复合事件是 DOM3级事件中新添加的事件,用于处理关于输入法的事件。
复合事件的事件对象有 data 属性。当触发 compositionstart 时它的值为当前正在编辑的文本,也就是将要被替换掉的文本。当 compositionend 触发时,它的值为本次输入所插入文本。
変动事件在 DOM 中的某一部分发生变化时触发。
在删除节点时会触发 DOMNodeRemoved 事件,这个事件的目标节点是被删除的节点,相关元素为目标节点的父节点。这个事件会冒泡,也就是说父元素能够接受到这个事件。移除节点时触发事件的顺序是 DOMNodeRemoved、DOMNodeRemovedFromDocument、DOMSubtreeModified。
通过 contextmenu 事件在用户调出右键菜单是触发,通过这个事件可以取消默认的右键菜单而提供自定义的菜单。这个事件隶属于鼠标事件,拥有鼠标事件的一系列属性。调出自定义菜单后不要忘记通过 onclick 取消菜单。
beforeunload 事件在用户卸载页面之前触发,弹出一个对话框询问是否退出。为了显示这个对话框,需要将 event.returnValue 设置为要显示给用户的字符串(IE 和 FireFox),并且作为函数值返回(Safari 和 Chrome)。
DOMContentLoaded 事件在页面的 DOM 树加载完成后触发,不理会图像、JavaScript 文件、CSS 文件是否加载完成。
DOM 文档的某些部分拥有 readystate 属性,这个属性可能包含以下值。
uninitialized:对象存在但尚未初始化。
loading:对象正在加载数据。
loaded:对象数据加载完成。
interactive:可以操作对象了,但对象还没有完全加载。
complete:对象已经加载完毕。
并非所有元素都会经历以上阶段,经历的顺序也不尽相同。
readystatechange 事件在对象的 readystate 属性改变时触发,这个事件的 event 对象不提供信息,没有目标对象
有的浏览器中都会有一个特性叫“往返缓存”(bfcache),浏览器会将整个页面的页面数据、DOM 状态、JavaScript 状态全部保存在内存中以加快“前进”与“后退”的速度。如果页面保存在 bfcache 中,那么再次打开页面时 load 事件就不会被触发。
pageshow 事件在页面显示时触发,无论页面是否保存在 bfcache 中这个事件的必须被添加在 window 上。pageshow 的 event 对象中包含一个 persisted 属性,这个属性表示这个页面是否被保存在了 bfcache 中。
pagehide 事件在浏览器卸载页面时触发,在 unload 之前触发。这个事件的 event 对象也包含 persisted 属性,表示页面是否将被保存在 bfcache 中。
hashchange 事件在 URL 中的 hash 变化时触发,这个事件必须添加给 window 对象。这个事件的 event 对象包含两个额外的属性,oldURL 和 newURL,分别保存着变化前后的完整 URL。
orientationchange 事件在设备旋转时触发,这个事件的 event 对象不提供信息,通过 window.orientation 属性可以访问到设备的状态。0表示正常模式,90表示左旋转的横屏模式,-90表示右旋转的横屏模式,180表示倒转模式。
deviceorientation 事件在设备方向变化时触发。设备的方向依靠 xyz 轴来确定。当设备放置于水平面时这三个值都为0。相对此时的屏幕来说,x 轴的方向是从左往右,y 轴方向是从下往上,z 轴方向是从后往前。这个事件的事件对象包含以下5个属性。
- alpha:在围绕 z 轴旋转时(即左右旋转时),y 轴的度数差,是一个介于0到360之间的浮点数。
- beta:在围绕 x 轴旋转时(即前后旋转时),z 轴的度数差,是一个介于-180到180之间的浮点数。
- gamma:在围绕 y 轴旋转时(即扭转设备时),z 轴的度数差,是一个介于-90到90之间的浮点数。
- absolute:表示设备返回的是否是一个绝对值。
- compassCalibrate:表示设备的指南针是否校准过。
devicemotion 事件在设备移动时触发,devicemotion 事件的事件对象包含以下属性。
- acceleration:一个包含 x、y、z 属性的对象,在不考虑重力的情况下,告诉你在每个方向上的加速度。
- accelerationIncludingGravity:考虑重力加速度。
- interval:表示触发的间隔。
- rotationRate:一个表示设备方向的对象。
以上事件的 event 对象都提供了鼠标事件中的各种属性。
- touches:表示当前跟踪的触摸操作的 Touch 对象的数组。
- targetTouch:特定于事件目标的 Touch 对象的数组。
- changeTouches:表示自上次触摸以来发生了什么改变的 Touch 对象的数组。
每个 Touch 对象都包含鼠标事件 event 对象中的一系列属性,此外还包含 identifier 属性,这个属性包含标识触摸的唯一 ID。
当两个手指触摸屏幕是就会产生手势,有三个手势事件。
只有当两个手指同时位于元素的范围中这些事件才会被触发,这些事件都冒泡。
手势事件出了包括标准的鼠标事件属性以外还包括两个额外的属性。
- rotation:表示手指变化引起的旋转角度。正值表示顺时针旋转,负值表示逆时针旋转。
- scale:表示两个手指间的距离的变化。这个值从1开始,随着距离的拉大而增大,随着距离的减小而缩小。
向页面中添加大量的事件处理程序会导致内存占用变多,性能变差,因此需要一些方法来提升性能。
许多事件可以冒泡直到 document 元素,因此可以为整个页面指定给一个事件处理程序,而不必为每一个元素指定事件处理程序。如:
<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(e){
e = e || window.event;
var target = EventUtil.getTarget();
switch(target.id) {
case 'doSomething':
//do something
break;
case 'goSomewhere':
//go somewhere
break;
case 'sayHi'
//say hi
break;
}
})
适合使用事件委托技术的事件有 click、mousedown、mouseup、keydown 和 keypress。
为了防止按钮的事件被多次触发,常用的方式是通过 innerHTML 将按钮转换成为一条消息。而在通过 innerHTML 移除按钮时,按钮所绑定的事件处理程序并不会被移除,而是留在内存中。因此在通过 innerHTML 移除按钮之前最好先移除事件处理程序。
此外,在卸载页面之前如果没有清理掉事件处理程序,那么它们就会滞留在内存中。因此最好的做法是在页面卸载之前通过 unload 事件移除所有事件处理程序。
在 document 对象上使用 createEvent()方法创建 event 对象,这个对象接受一个参数,即要创建的事件类型的字符串。这个字符串可以是下列几个字符串之一。在 DOM2级中这些字符串采用的都是英文的复数形式。
- UIEvent:一般化的 UI 事件。
- MouseEvent:一般化的鼠标事件。
- keyboardEvent:一般化的键盘事件,只在 DOM3级中支持。
- MutationEvent:一般化的 DON 变动事件。
- HTMLEvent:一般化的 HTML 事件。
创建了 event 对象之后,调用每种 event 对象的专属方法来初始化这个对象。最后将 event 对象传入 dispatchEvent()就可以触发该事件。
为 createEvent()传入“MouseEvent”,返回的对象有一个 initMouseEvent()方法。这个方法接受15个参数,与鼠标事件中的典型属性一一对应。
- type:表示要触发的事件类型。
- bubbles:表示事件是否应该冒泡。为了精确模拟鼠标事件这个值应该为 true。
- cancleable:为了精确模拟鼠标事件,这个值应该为 true。
- view:与事件关联的视图,这个的参数几乎总该被设置为 document.defaultView。
- detail 通常设置为0。
- screenX
- screenY
- clientX
- clientY
- ctrlKey
- altKey
- shiftKey
- metaKey
- button:表示按下了哪个鼠标键。
- relatedTarget:只在模拟 mouseover 和 mouseout 时使用。
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);
//触发事件
vtn.dispatchEvent(event)
在 Chrome 和 Oprea 浏览器中调用 createEvent()事件并传入“KeyboardEvent”就可以创建一个键盘事件,返回的对象中包含一个 initKeyboardEvent()方法,这个方法接受下列参数。
- type
- bubbles:为了精确模拟键盘事件这个值应该为 true。
- cancleable:为了精确模拟键盘事件这个值应该为 true。
- view:这个参数几乎总要设置为 document.defaultView。
- key:表示按下的键的键码。
- location
- modifiers:空格分割的修改键列表。
- repeat:在一行中这个键按了多少次。
DOM3级中不提倡使用 keypress 事件,因此只能利用这个技术来模拟 keydown 和 keyup 事件。
event.initKeyboardEvent("keydown", true, true, document.defaultView, "a", 0, "shift", 0);
在 Firefox 浏览器中调用 createEvent()并传入“KeyEvent”创建一个键盘事件。返回的对象包含一个 initKeyEvent()方法,这个方法接受以下参数。
- type
- bubbles:为了精确模拟键盘事件这个值应该为 true。
- cancleable:为了精确模拟键盘事件这个值应该为 true。
- view:这个参数几乎总要设置为 document.defaultView。
- ctrlKey
- altKey
- shiftKey
- metaKey
- keyCode:表示按下或释放的键的键码。
- charCode:通过按键生成的字符的 ASCII 编码。
event.initKeyEvent("keypress", true, true, document.defaultView, false, false, false, false, 65, 65);
调用 createEvent()并传入“MutationEvent”可以创建变动事件对象,这个对象包含 initMutationEvent()方法,这个方法接受以下参数。
- type:事件类型,包括 DOMSubtreeModified、DOMNodeInserted、DOMNodeRemoved、DOMAttrModified 和 DOMCharacterDataModified
- bubbles
- cancleable
- relateNode
- preValue:对于 DOMAttrModified 更改前的值,默认为""。
- newValue:默认为""。
- attrName:属性名,默认为""。
- attrChange:DOMAttrModified 的改变类型,包括 MODIFICATION、ADDITION、REMOVAL,默认为0。
调用 createEvent()并传入“CustomEvent”可以创建自定义事件,返回的对象拥有 initCustomEvent()方法,这个方法接受4个参数。
- type
- bubbles
- cancleable
- detail
在 IE 浏览器中需要先创建一个通用事件,然后再对事件对象添加事件特有的信息。
var textbox = document.getElementById('myTextbox');
var event = document.createEvent('Event');
event.initEvent(type, bubbles, cancleable);//这是需要填写的
event.view = document.defaultView;
event.altKey = false;
event.crtlKey = false;
event.shiftKey = false;
event.metaKey = false;
event.keyCode = 65;
event.charCode = 65;