JavaScript 事件是由访问 Web 页面的用户引起的一系列的操作,当用户执行某些操作的时候,再去执行一系列代码。
事件分类
JavaScript 可以处理的事件类型分为鼠标事件,键盘事件,HTML 事件。
所有的事件处理函数都有两部分组成:on+事件类型名称。
鼠标事件:
-
click:当用户单击鼠标按钮或按下回车键时触发。
-
dblclick:当用户双击鼠标按钮时触发。
-
mousedown:当用户按下鼠标按钮时触发。
-
mouseup:当用户释放鼠标按钮时触发。
-
mouseenter:当鼠标进入到某个元素里时触发。只有在鼠标进入元素时触发。不会冒泡。
-
mouseleave:当鼠标移出某个元素时触发。只有当鼠标指针离开了目标元素以及目标元素的所有子元素后才会触发。不会冒泡。
-
mouseover:当鼠标移动到某个元素上时触发。在鼠标进入或者在元素内移动时都会触发。会冒泡。
-
mouseout:当鼠标移出某个元素上时触发。只要鼠标指针离开了目标元素或者目标元素的所有子元素中的任何一个就会被触发,即使鼠标指针还在目标元素内。会冒泡。
由于 mouseover、mouseout 支持事件冒泡,mouseenter、mouseleave 不支持事件冒泡,因此在子元素上进入或离开的时候会触发其父元素的 mouseover、mouseout 事件,但是却不会触发 mouseenter、mouseleave 事件。
-
mousemove:当鼠标在元素上移动时触发。
-
mousewheel:当鼠标滚轮在元素上滚动时触发。
Firefox 支持一个 DOMMouseScroll 的类似事件,鼠标滚轮有关的信息保存在 detail 属性中, 当向前滚动时,值是 -3 的倍数,向后滚动时,值是 3 的倍数。
键盘事件:
- keydown:当用户按下键盘上任意键时触发。
- keyup:当用户释放键盘上的任意键时触发。
- keypress:当用户按下键盘上任意字符键时触发。
字符键:abc、123、标点符号键等(不包括 shift 等)。
HTML 事件:
- load:当页面完全加载后在 window 上触发,或者当框架集加载完毕后在框架集上触发。
window.onload=function(){}
- unload:当页面完全卸载后在 window 上触发(关闭浏览器或者刷新页面),或者当框架集卸载后在框架集上触发。
- resize:当窗口或框架的大小变化时在 window 或框架上触发。
- move:当浏览器窗口移动时触发。
- scroll:当用户滚动带滚动条的元素时触发。
- focus:当元素获得焦点时触发。
- blur:当元素失去焦点时触发。
- abort:当图像加载被中断时触发。
- submit:当用户点击提交按钮时在
<form>
元素上触发。没有 form 的话,submit 无法提交。
form.onsubmit=function(){}
- reset:当用户点击重置按钮时在
<form>
元素上触发。form.onreset=function(){}
- select:当用户选定文本框中的一个或多个字符时触发。
- change:当文本框内容改变时触发。
事件对象 event:
每当触发一个事件,就会产生一个事件对象 event,该对象包含着所有与事件有关的信息。
获取 event 对象:
在 W3C 中 event 对象会作为默认参数被传递给事件处理函数;在 IE 中 event 对象是 window 对象的一个属性。
event 对象的属性:
- type:事件类型。
- target:触发事件的对象。
currentTarget:绑定事件的对象,恒等于 this。//考虑浏览器的兼容性: document.onclick=function(evt){ var e=evt||window.event; var obj=e.target; alert(obj.nodeName); }
- 鼠标按钮信息:对于 mousedown 和 mouseup 事件,其 event 对象存在一个 button 属性。
- 在 W3C 中,返回的属性值为 0,表示按下或释放了主鼠标按钮;返回的属性值为 1,表示按下或释放了中间的鼠标按钮;返回的属性值为 2,表示按下或释放了次鼠标按钮。
- 在 IE 中,返回的属性值为 0,表示没有按下或释放鼠标按钮;返回的属性值为 1,表示按下或释放了主鼠标按钮;返回的属性值为 2,表示按下或释放了次鼠标按钮;返回的属性值为 4,表示按下或释放了中间的鼠标按钮。
主鼠标按钮是鼠标左键,中间的鼠标按钮是鼠标滚轮,次鼠标按钮是鼠标右键。
//跨浏览器鼠标按钮 function getButton(evt){ if(evt){ return evt.button; }else if(window.event){ switch(window.event.button){ case 1:return 0; case 4:return 1; case 2:return 2; } } } window.onload=function(){ if(alert(getButton(evt))==0){ alert(“左键”); }else if(alert(getButton(evt))==1){ alert(“中键”); } if(alert(getButton(evt))==2){ alert(“右键”); } }
- 鼠标滚轮信息:
- wheelDelta:当用户向前滚动鼠标滚轮时,wheelDelta 是 120 的倍数;当用户向后滚动鼠标滚轮时,wheelDelta 是 -120 的倍数。
兼容性:Opera9.5 之前的版本,wheelDelta 值的正负号是颠倒的。
- wheelDelta:当用户向前滚动鼠标滚轮时,wheelDelta 是 120 的倍数;当用户向后滚动鼠标滚轮时,wheelDelta 是 -120 的倍数。
- 键盘信息:
-
keyCode:获得键盘的按键信息。
keydown、keyup 支持特殊按键(command、shift 等),keydown、keyup下 keyCode 所代表的按键不区分大小写。
keypress 不支持特殊按键,且 keypress 下 keyCode 区分大小写。var keyCode = event.keyCode; 119 // 119 是字符 "w"
-
- 屏幕坐标信息:
- offsetX、offsetY:鼠标点击位置相对于触发事件的对象的水平和垂直距离(灰色方块即为事件触发对象)
- clientX、clientY:鼠标点击位置相对于浏览器可视区的水平和垂直距离(不会计算水平滚动和垂直滚动的距离)。
- screenX、screenY:鼠标点击位置相对于电脑屏幕左上角的水平和垂直距离。
- pageX、pageY:
pageX = clientX + 水平滚动的距离
(clientX+X1),pageY = clientY + 垂直滚动的距离
(clientY+Y1)
事件模型:
事件模型可以分为三类:原始事件模型(DOM0)、DOM2 事件模型、IE 事件模型。
原始事件模型(DOM0):不推荐使用
- 注册事件:
- 将 JavaScript 代码作为 HTML 标签的性质:
<button id="demo" type="button" onclick="doSomeTing()" />
。 - 将事件处理函数作为 JavaScript 对象的属性:
document.getElementsById("demo").onclick = doSomeTing();
。
- 将 JavaScript 代码作为 HTML 标签的性质:
- 解除事件:将 null 复制给事件处理函数。
document.getElementById("demo").onclick = null;
。
优点:所有浏览器都兼容。
缺点:
- 一个类型的事件在一个 DOM 对象上只能注册一个,如果注册了多个,则会发生覆盖,只执行最后一个事件。例如
a.onclick = func1;;a.onclick = func2;
,将只会执行 func2 中的内容。 - 没有事件流,事件一旦发生将马上进行处理,无法通过事件冒泡、事件委托等机制完成更多事情。
DOM2 事件模型:
DOM2 事件模型是 W3C 制定的标准模型,现代浏览器(IE6~8 除外)都已经遵循这个规范。
- 注册事件:
addEventListener("eventType","handler","true|false");
。eventType 是指事件类型,不加 on 前缀;handler 是处理函数;第三个参数用来指定是否在捕获阶段进行处理,一般为 false。 - 解除事件:
removeEventListner("eventType","handler","true|false");
。
在 DOM2 事件模型中,一次事件发生的过程:
在 DOM2 事件模型中,一次事件的发生包含三个过程:事件捕获阶段、事件目标阶段、事件冒泡阶段。
- 事件捕获阶段:当某个元素触发某个事件,顶层对象 document 就会发出一个事件流,随着 DOM 树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。
- 事件目标阶段:当到达目标元素之后,执行目标元素的该事件相应的处理函数。
- 事件冒泡阶段:从目标元素开始,往顶层元素传播。途中如果有节点监听了相同的事件,绑定的事件处理函数将会被一并触发。
所有的事件类型都会经历事件捕获,但是只有部分事件会经历事件冒泡阶段。例如 submit 事件就不会被冒泡。
IE 事件模型:
IE 的事件模型只有两步:先执行元素的监听函数,然后事件沿着父节点一直冒泡到 document。
- 注册事件:
attachEvent( "eventType","handler")
,其中 evetType 为事件的类型要加 on。 - 解除事件:
detachEvent("eventType","handler" )
。
IE 的事件模型已经可以解决原始事件模型的缺点,但其自己的缺点就是兼容性,只有 IE 系列浏览器才可以使用。
//兼容所有浏览器:
var demo = document.getElementById('demo');
if(demo.attachEvent){
demo.attachEvent('onclick',func);
}else{
demo.addEventListener('click',func,false);
}
事件冒泡:
事件冒泡 :当一个元素接收到事件的时候,会把他接收到的事件一级一级向上传递。
传递的仅仅是事件,并不传递所绑定的事件处理函数。所以如果父级没有监听事件,就算传递了事件,也不会有什么表现,但事件确实传递了。
<div id="div1">
<div id="div2"></div>
</div>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
div2.onclick = function(){alert(1);};
div1.onclick = function(){alert(2);};
在 div2 里面点击的时候,会发现弹出了一次 1,接着又弹出了 2,这说明点击的时候,不仅 div2 的事件被触发了,它的父级 div1 的事件也触发了,这种现象就叫做冒泡。
阻止事件冒泡:`
- W3C:
event.stopPropagation();
。 - IE:
event.cancelBubble = true
。
阻止默认行为:
- W3C:
event.preventDefault();
。 - IE:
event.returnValue = false;
。
既阻止事件冒泡,又阻止默认行为:
return false;
。
事件委托(事件代理):
通过事件冒泡的原理,将事件处理函数绑定在父级上,利用父级去触发子级的事件。
事件委托是通过事件冒泡实现的,所以如果子级的元素阻止了事件冒泡,那么事件委托也将失效。
使用事件委托的原因:
例如:有 100 个 li,每个 li 都有相同的 click 点击事件,用 for 循环的方法,来遍历所有的 li,给它们添加事件。
这样操作存在的严重的性能问题:需要访问大量的 dom 来给它们绑定事件,访问 dom 的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间。
<ul id="ul">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
window.onload = function(){
var lis = document.getElementsByTagName('li');
for(var i=0;i<li.length;i++){
lis[i].onclick = function(){
alert("li);
}
}
}
使用事件委托的好处:
使用事件委托,避免对每个子元素添加事件监听器,只有父元素与 DOM 存在交互,其他的操作都是在 JS 虚拟内存中完成的,减少操作 DOM 节点的次数,从而减少浏览器的重绘和重排,大大提高了性能。
// 用父级 ul 做事件处理,当 li 被点击时,由于冒泡原理,事件就会冒泡到 ul 上,因为 ul 上有绑定点击事件的事件处理函数,所以事件就会触发
window.onload = function(){
var ul = document.getElementById("ul");
ul.onclick = function(ev){
var ev = ev || window.event;
if(target.nodeName.toLowerCase() == 'li'){
alert('li"');
}
}
}