事件是将JavaScript与网页联系在一起的主要方式。"DOM3级事件"规范和HTML5定义了常见的大多数事件。
即使有规范定义了基本事件,但多数浏览器仍然在规范之外实现了自己的专有事件,从而为开发人员提供更多掌握用户
交互的手段。有些专有事件与特定设备关联,例如移动Safari中的orientationchange事件就是特定关联ios设备的。
在使用事件时,需要考虑如下一些内存与性能方面的问题。
1)有必要限制一个页面中事件处理程序的数量,数量太多会导致占用大量内存,而且也会让用户感觉页面反应不够灵敏。
2)建立在事件冒泡机制至上的事件委托技术,可以有效地减少事件处理程序的数量。
3)建议在浏览器卸载页面之前移除页面中的所有事件处理程序。
可以使用JavaScript在浏览器中模拟事件。"DOM2级事件"和"DOM3级事件"规范规定了模拟事件的方法,为模拟各种有定义的事件提供了方便。此外,通过组合使用一些技术,还可以在某种程度上模拟键盘事件。IE8及之前版本同样支持事件模拟,只不过模拟的过程有些诧异。
事件是JavaScript中最重要的主题之一,深入理解事件的工作机制以及它们对性能的影响至关重要。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件</title>
</head>
<body>
<button id="myBtn">DOM0级事件处理程序</button>
<button id="myBtn2">DOM2级事件处理程序</button>
<button id="myBtn3">IE事件处理程序</button>
<button id="myBtn4">跨浏览器的事件处理程序</button>
<button id="myBtn5">DOM中的事件对象</button>
<button id="myBtn6">处理多个事件</button>
<a href="www.baidu.com" id="myLink">阻止默认行为</a>
<button id="myBtn7">取消事件的冒泡或捕获</button>
<button id="myBtn8">测试事件位于事件流的哪个阶段</button>
<input type="text" id="myText"/>
<div id="myDiv" style="width: 300px;height: 300px;border: 1px solid red"></div>
<ul id="myMenu" style="position: absolute;visibility: hidden;background-color: silver">
<li><a href="http://www.baidu.com">百度</a></li>
<li><a href="http://www.sina.com.cn">新浪</a></li>
<li><a href="http://www.qq.com">腾讯</a></li>
</ul>
<ul id="myLinks">
<li id="goSomewhere">去哪儿</li>
<li id="doSomething">做什么</li>
<li id="sayHi">你好</li>
</ul>
<div id="myDiv2">
<input type="button" value="点我" id="clickMe"/>
</div>
<button id="myBtn9">模拟鼠标事件</button>
<script src="l13.js"></script>
</body>
</html>
/*
* 事件
*/
function cl(x){
console.log(x);
}
/**
* 13.1 事件流
*/
//13.1.1 事件冒泡
//IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。
//13.1.2 事件捕获(很少使用)
//事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。
//13.1.3 DOM事件流
//"DOM2级事件"规定的事件流包括:事件捕获阶段、处于目标阶段和事件冒泡阶段。
/**
* 13.2 事件处理程序
*/
//13.2.1 HTML事件处理程序(要摒弃)
//13.2.2 DOM0级事件处理程序
var btn=document.getElementById("myBtn");
btn.οnclick=function(){
cl(this.id);//=>"myBtn"
}
btn.οnclick=null;//删除事件处理程序
//13.2.3 DOM2级事件处理程序 (IE9+)
var btn2=document.getElementById("myBtn2");
var handler=function(){
cl(this.id);//=>"myBtn2"
}
btn2.addEventListener("click",handler,false);
btn2.addEventListener("click",function(){//可添加多个事件
cl("hello world");//=>"hello world"
},false);
btn2.removeEventListener("click",handler,false);//移除事件
//13.2.4 IE事件处理程序
//var btn3=document.getElementById("myBtn3");
//var handler2=function(){
// cl(this===window);
//};
//btn3.attachEvent("onclick",handler2);
//btn3.detachEvent("onclick",handler2);
//13.2.5 跨浏览器的事件处理程序
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 btn4=document.getElementById("myBtn4");
var handler4=function(){
cl("跨浏览器处理事件程序");//=>"跨浏览器处理事件程序"
};
EventUtil.addHandler(btn4,"click",handler4);
EventUtil.removeHandler(btn4,"click",handler4);
/**
* 13.3 事件对象
*/
//13.3.1 DOM中的事件对象
//event对象
var btn5=document.getElementById("myBtn5");
btn5.οnclick=function(event){
cl(event.type);//=>"click"
}
btn5.addEventListener("click",function(event){
cl(event.type);//=>"click"
},false);
//注意event.currentTarget与event.target的区别
btn5.addEventListener("click",function(event){
cl(event.currentTarget===this);//=>true
cl(event.target===this);//=>true
});
document.body.οnclick=function(event){
cl(event.currentTarget===document.body);//=>true
cl(this===document.body);//=>true
cl(event.target===document.getElementById("myBtn5"));//=>true
}
var btn6=document.getElementById("myBtn6");
var handler6=function(event){
switch(event.type){
case "click":
cl("点击事件");
break;
case "mouseover":
event.target.style.backgroundColor="red";
break;
case "mouseout":
event.target.style.backgroundColor="";
break;
}
};
btn6.οnclick=handler6;
btn6.οnmοuseοver=handler6;
btn6.οnmοuseοut=handler6;
//阻止特定事件的默认行为:preventDefault()方法
var link=document.getElementById("myLink");
link.οnclick=function(event){
event.preventDefault();
}
//stopPropagation()方法用于立即停止事件在DOM层次中的传播
var btn7=document.getElementById("myBtn7");
btn7.οnclick=function(event){
cl("Click");
event.stopPropagation();
}
document.body.οnclick=function(event){
cl("Body click");
}
//事件对象的eventPhase属性,用来确定事件当前正位于事件流的哪个阶段
//捕获=1,目标=2,冒泡=3
var btn8=document.getElementById("myBtn8");
btn8.οnclick=function(event){
cl(event.eventPhase);//=>2
}
document.body.addEventListener("click",function(event){
cl(event.eventPhase);//=>1
},true);
document.body.οnclick=function(event){
cl(event.eventPhase);//=>3
}
//13.3.2 IE中的事件对象
//DOM0级时:event=window.event
//DOM2级时:event对象与DOM中相同
//srcElement与DOM中target相同
//event.returnValue设置为false来阻止默认行为
//event.cancelBubble设置为true来取消事件冒泡
//13.3.3 跨浏览器的事件对象
var EventUtil={
//添加事件监听
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);//w3c
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);//IE
}else{
element["on"+type]=handler;//低版本浏览器
}
},
//获取事件元素
getEvent:function(event){
return event?event:window.event;
},
//获取事件目标
getTarget:function(event){
return event.target||event.srcElement;
},
//阻止默认事件
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();//W3C
}else{
event.returnValue=false;//IE
}
},
//移除事件监听
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);//W3C
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);//IE
}else{
element["on"+type]=null;//低版本浏览器
}
},
//阻止冒泡
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();//W3C
}else{
event.cancelBubble=true;//IE
}
},
//相关元素属性
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;
}
},
//获取鼠标按钮属性
getButton:function(event){
if(document.implementation.hasFeature("MouseEvents","2.0")){
return event.button;
}else{//IE8及以下
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;
}
}
},
//获取鼠标滚轮事件属性
getWheelDelta:function(event){
if(event.wheelDelta){
return (client.engine.opera && client.engine.opera<9.5 ?
-event.wheelDelta:event.wheelDelta);
}else{
return -event.detail*40;
}
},
//获取字符编码属性
getCharCode:function(event){
if(typeof event.charCode=="number"){
return event.charCode;
}else{
return event.keyCode;
}
}
};
/**
* 13.4 事件类型
*/
//13.4.1 UI事件
//load、unload、abort、error、select、resize、scroll
//13.4.1.1 load事件
//当页面完全加载后(包含所有图像、JavaScript文件、CSS文件等外部资源)触发window上面的load事件
EventUtil.addHandler(window,"load",function(event){
cl("文档加载完成");
});
EventUtil.addHandler(window,"load",function(event){
var image=document.createElement("img");
EventUtil.addHandler(image,'load',function(event){
event=EventUtil.getEvent(event);
cl(EventUtil.getTarget(event).src);
});
document.body.appendChild(image);
//image.src="about_2.jpg";
});
EventUtil.addHandler(window,'load',function(){
var image=new Image();
EventUtil.addHandler(image,'load',function(event){
cl("图片加载完毕");
});
image.src="head.jpg";
});
//13.4.1.2 unload事件
//文档被完全卸载后触发,利用这个事件最多的情况是清除引用,以避免内存泄露
EventUtil.addHandler(window,'unload',function(event){
cl("页面卸载了");
});
//13.4.1.3 resize事件
//当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件
EventUtil.addHandler(window,'resize',function(event){
cl("窗口大小改变了");
});
//13.4.1.4 scroll事件
//当用户滚动带滚动条的元素中的内容时,在该元素上面触发
EventUtil.addHandler(window,'scroll',function(event){
if(document.compatMode=="CSS1Compat"){
cl(document.documentElement.scrollTop);
}else{
cl(document.body.scrollTop);
}
});
//13.4.2 焦点事件
//blur、focus、focusin、focusout
//13.4.3 鼠标与滚轮事件
//click、dbclick、mousedown、mouseenter、mouseleave、mousemove、mouseout、mouseover、mouseup
//13.4.3.1 客户区坐标位置
//13.4.3.2 页面坐标位置
//13.4.3.3 屏幕坐标位置
var div=document.getElementById("myDiv");
EventUtil.addHandler(div,'click',function(event){
event=EventUtil.getEvent(event);
cl("点击的视口位置:"+event.clientX+","+event.clientY);
cl("点击的页面位置:"+event.pageX+","+event.pageY);
cl("点击的屏幕位置:"+event.screenX+","+event.screenY);
});
//13.4.3.4 修改键
//shiftKey、ctrlKey、altKey和metaKey
EventUtil.addHandler(div,'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");
}
cl("Keys:"+keys.join(","));
});
//13.4.3.5 相关元素
//relatedTarget属性,只对于mouseover和mouseout事件才包含值
var div=document.getElementById("myDiv");
EventUtil.addHandler(div,"mouseout",function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
var relatedTarget=EventUtil.getRelatedTarget(event);
cl("鼠标从 "+target.tagName+ "移到了"+relatedTarget.tagName);
});
//13.4.3.6 鼠标按钮
var div=document.getElementById("myDiv");
EventUtil.addHandler(div,"mousedown",function(event){
event=EventUtil.getEvent(event);
cl(EventUtil.getButton(event));
});
//13.4.3.7 更多的事件信息
//detail属性
//13.4.3.8 鼠标滚轮事件
//mousewheel事件 DOMMouseScroll事件
(function(){
function handleMouseWheel(event){
event=EventUtil.getEvent(event);
var delta=EventUtil.getWheelDelta(event);
cl(delta);
}
EventUtil.addHandler(document,"mousewheel",handleMouseWheel);
EventUtil.addHandler(document,"DOMMouseScroll",handleMouseWheel);
})();
//13.4.3.9 触摸设备
//13.4.3.10 无障碍性问题
//13.4.4 键盘与文本事件
//keydown事件、keypress事件、keyup事件、textInput事件
//13.4.4.1 键码:keyCode属性
var textBox=document.getElementById("myText");
EventUtil.addHandler(textBox,"keyup",function(event){
event=EventUtil.getEvent(event);
cl(event.keyCode);
});
//13.4.4.2 字符编码:charCode属性
var textBox=document.getElementById("myText");
EventUtil.addHandler(textBox,"keypress",function(event){
event=EventUtil.getEvent(event);
cl(EventUtil.getCharCode(event));
});
//13.4.4.3 DOM3级变化
//存在浏览器兼容性问题
//13.4.4.4 textInput事件
var textBox=document.getElementById("myText");
EventUtil.addHandler(textBox,"textInput",function(event){
event=EventUtil.getEvent(event);
cl(event.data);
});
//13.4.4.5 设备中的键盘事件
//13.4.5 复合事件(IE9+支持)
//compositionstart事件、compositionupdate事件、compositionend事件
var textBox=document.getElementById("myText");
var isSupported=document.implementation.hasFeature("CompositionEvent","3.0");
if(isSupported){//IE9+
EventUtil.addHandler(textBox,"compositionstart",function(event){
event=EventUtil.getEvent(event);
cl(event.data);
});
EventUtil.addHandler(textBox,"compositionupdate",function(event){
event=EventUtil.getEvent(event);
cl(event.data);
});
EventUtil.addHandler(textBox,"compositionend",function(event){
event=EventUtil.getEvent(event);
cl(event.data);
});
}else{
cl("该浏览器不支持复合事件");
}
//13.4.6 变动事件
//DOMSubtreeModified、DOMNodeInserted、DOMNodeRemoved
var isSupported=document.implementation.hasFeature("MutationEvents","2.0");
//13.4.6.1删除节点
//13.4.6.2插入节点
//13.4.7 HTML5事件
//13.4.7.1 contextmenu事件:上下文菜单
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";
})
});
//13.4.7.2 beforeunload事件:卸载页面前
EventUtil.addHandler(window,"beforeunload",function(event){
event=EventUtil.getEvent(event);
var message="真的要卸载吗?";
event.returnValue=message;
return message;
})
//13.4.7.3 DOMContentLoaded事件:DOM树加载之后触发
EventUtil.addHandler(document,"DOMContentLoaded",function(event){
cl("内容加载完成");
});
//13.4.7.4 readystatechange事件
EventUtil.addHandler(window,"load",function(){
var script=document.createElement("script");
EventUtil.addHandler(script,"readystatechage",function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
if(target.readyState=="loaded" || target.readyState=="complete"){
EventUtil.removeHandler(target,"readystatechange",arguments.callee);
cl("script加载完成");
}
});
script.src="example.js";
document.body.appendChild(script);
})
//13.4.7.5 pageshow和pagehide事件
(function(){
var showCount=0;
EventUtil.addHandler(window,"load",function(){
cl("Load fired");
});
EventUtil.addHandler(window,"pageshow",function(){
showCount++;
cl("Show has been fired "+showCount+" times");
})
})();
//13.4.7.6 hashchange事件
EventUtil.addHandler(window,"hashchangge",function(event){
cl("准确的hash:"+location.hash);
})
//13.4.8 设备事件
//13.4.8.1 orientationchange事件
EventUtil.addHandler(window,"load",function(event){
var div=document.getElementById("myDiv");
div.innerHTML="现在的方向是"+window.orientation;
EventUtil.addHandler(window,"orientationchange",function(event){
div.innerHTML="现在的方向是"+window.orientation;
})
})
//13.4.8.2 mozOrientation事件
//13.4.8.3 deviceorientation事件
EventUtil.addHandler(window,"deviceorientation",function(event){
var output=document.getElementById("output");
output.innerHTML="Alpha="+event.alpha+",Beta="+event.beta+",Gamma="+event.gamma+"<br/>";
});
EventUtil.addHandler(window,"deviceorientation",function(event){
var arrow=document.getElementById("arrow");
arrow.style.webkitTransform="rotate("+Math.round(event.alpha)+"deg)";
});
//13.4.8.4 devicemotion事件
EventUtil.addHandler(window,"devicemotion",function(event){
var output=document.getElementById("output");
if(event.rotationRate!==null){
output.innerHTML="Alpha="+event.rotationRate.alpha+",Beta="+event.rotationRate.beta+",Gamma="+event.rotationRate.gamma+"<br/>";
}
})
//13.4.9 触摸与手势事件
//13.4.9.1 触摸事件
//13.4.9.2 手势事件
//gesturestart、gesturechange、gestureend
/**
* 13.5 内存和性能
*/
//13.5.1 事件委托
var list=document.getElementById("myLinks");
EventUtil.addHandler(list,"click",function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
switch(target.id){
case "doSomething":
document.title="我改了标题";
break;
case "goSomewhere":
location.href="http://www.baidu.com";
break;
case "sayHi":
cl("hi");
break;
}
});
//13.5.2 移除事件处理程序
var btn=document.getElementById("clickMe");
btn.οnclick=function(){
//先执行某些操作
btn.οnclick=null;
document.getElementById("myDiv2").innerHTML="Processing...";
}
/**
* 13.6 模拟事件
*/
//13.6.1 DOM中的事件模拟
//13.6.1.1 模拟鼠标事件
var btn9=document.getElementById("myBtn9");
//创建事件对象
var event=document.createEvent("MouseEvents");
//初始化事件对象
event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
//触发事件
btn9.dispatchEvent(event);
//13.6.1.2 模拟键盘事件
//13.6.1.3 模拟其他事件
//13.6.1.4 自定义DOM事件
var div=document.getElementById("myDiv"),event;
EventUtil.addHandler(div,"myevent",function(event){
cl("DIV: "+event.detail);
});
EventUtil.addHandler(document,"myevent",function(event){
cl("DOCUMENT: "+event.detail);
});
if(document.implementation.hasFeature("CustomEvents","3.0")){
event=document.createEvent("CustomEvent");
event.initCustomEvent("myevent",true,false,"hello world!");
div.dispatchEvent(event);
}
//13.6.2 IE中的事件模拟