JavaScript 事件绑定及深入
事件绑定分为两种:
一种是传统事件绑定(内联模型/脚本模型);上一章内容;
一种是现代事件绑定(DOM2级模型);现代事件绑定在传统事件绑定基础上提供了更强大的功能;
一传统事件绑定的问题
//脚本模型将一个函数赋值给一个事件处理函数;
varbox=document.getElementById('box');//获取元素;
box.onclick=function(){//元素点击触发事件;
alert('Lee');
}
//问题一:一个事件处理函数触发两次事件;
window.onload=function(){//第一组程序;
alert('Lee');
}
window.onload=function(){//第二组程序;
alert('Mr.Lee');
}
//PS:当两组程序同时执行的时候,后面一个会把前面一个完全覆盖;
//导致前面的window.onload完全失效了;
//解决方案:
window.onload=function(){//第一组事件处理程序,会被覆盖;
alert('Lee');
}
if(typeofwindow.onload=='function'){//判断之前是否有window.onload;
varsaved=null;//创建一个保存器;
saved=window.onload;//把之前的window.onload保存起来;
}
window.onload=function(){//下一个要执行的事件;
//saved()=window.onload=function
if(saved)saved();//判断之前是否有事件,如果有则先执行之前保存的事件;
alert('Mr.Lee');//执行本事件的代码;
}
//问题二:事件切换器
box.onclick=boBlue;//第一次执行toBlue();
functiontoRed(){
this.className='red';
this.onclick=toBlue;//第三次执行roBlue(),然后来回切换;
}
functiontoBlue(){
this.className='blue';
this.onclick=toRed;//第二次执行toRed();
}
//这个切换器在扩展的时候,会出现一些问题:
1.如果增加一个执行函数,那么会被覆盖;
box.onclick=toAlert;//被增加的函数;
box.onclick=toBlue;//toAlert被覆盖了;
2.如果解决覆盖问题,就必须包含同时执行;
box.onclick=function(){//包含进去,但可读性降低;
toAlert();//第一次不会被覆盖,但第二次又被覆盖;
toBlue.call(this);//还必须把this传递到切换器里;
}
//综上三个问题:覆盖问题/可读性问题/this传递为题;
//我们创建一个自定义事件处理函数;
functionaddEvent(obj,type,fn){//取代传统事件处理函数;
varsaved=null;//保存每次触发的事件处理函数;
if(typeofobj['on'+type]=='function'){//判断是不是存在事件;
saved=obj['on'+type];//如果有,保存起来;
}
obj['on'+type]=function(){//然后执行;
if(saved)saved();//执行上一个;
fn.call(this);//执行函数,把this传递进去;
}
}
addEvent(window,'load',function(){
alert('Lee');//可以执行;
});
addEvent(window.'load',function(){
alert('Mr.Lee');//可以执行;
})
//用自定义事件函数注册到切换器上查看效果:
addEvent(window,'load',function(){
varbox=document.getElementById('box');
addEvent(box,'click',toBlue);
});
functiontoRed(){
this.className='red';
addEvent(this,'click',toBlue);
}
functiontoBlue(){
this.className='blue';
addEvent(this,'click',toRed);
二W3C事件处理函数
//"DOM2级事件"定义了两个方法,用于添加事件和删除事件的处理程序:addEventListener()和removeEventListener();
//所有DOM节点中都包含这两个方法,并且它们都接收3个参数:事件名/函数/冒泡或捕获的布尔值(true表示捕获,false表示冒泡);
window.addEventListener('load',function(){
alert('Lee');
},false);
window.addEventListener('load',function(){
alert('Mr.Lee');
},false);
//PS:W3C的事件绑定好处:1.不需要自定义了;2.可以屏蔽相同的函数;3.可以设置冒泡和捕获;
window.addEventListener('load',init,false);//第一次执行了;
window.addEventListener('load',init,false);//第二次被屏蔽了;
functioninit(){
alert('Lee');
}
//事件切换器
window.addEventListener('load',function(){
varbox=document.getElementById('box');
box.addEventListener('click',function(){//不会被覆盖/误删;
alert('Lee');
},false);
box.addEventListener('click',toBlue,false);//引入切换;
},false);
functiontoRed(){
this.className='red';
this.removeEventListener('click',toRed,false);//移除事件处理函数;
this.addEventListener('click',toBlue,false);//添加需要切换的事件处理函数;
}
functiontoBlue(){
this.className='blue';
this.removeEventListener('click',toBlue,false);
this.addEventListener('click',toRed,false);
}
//设置冒泡和捕获阶段
document.addEventListener('click',function(){
alert('document');
},true);//设置为捕获;
document.addEventListener('click',function(){
alert('Lee');
},false);//设置为冒泡;
三IE事件处理函数
//IE中实现了与DOM中类似的两个方法:attachEvent()和detachEvent();
//这两个方法接收相同的参数:事件名和函数;
//在使用这两组函数的时候,区别:
//1.IE不支持捕获,只支持冒泡;
//2.IE添加事件不能屏蔽重复的函数;
//3.IE中的this指向的是window而不是DOM对象;
//4.在传统事件上,IE是无法接受到event对象的;但使用了attachEvent()却可以;
window.attachEvent('onload',function(){
varbox=document.getElementById('box');
box.attachEvent('onclick',toBlue);
});
functiontoRed(){
varthat=window.event.srcElement;
that.className='red';
that.detachEvent('onclick',toRed);
that.attachEvent('onclick',toBlue);
}
functiontoBlue(){
varthat=window.event.srcElement;
that.className='blue';
that.detachEvent('onclick',toBlue);
that.attachEvent('onclick',toRed);
}
//PS:IE不支持捕获;
//IE不能屏蔽;
//IE不能传递this,可以call过去;
//在传统绑定上,IE是无法像W3C那样通过传参接受event对象;但如果使用了attachEvent()却可以;
box.onclick=function(evt){
alert(evt);//undefined;
}
box.attachEvent('onclick',function(evt){
alert(evt);//object;
alert(evt.type);//click;
});
//兼容IE和W3C的事件切换器函数;
functionaddEvent(obj,type,fn){//添加事件处理程序兼容;
if(obj.addEventListener){
obj.addEventListener(type,fn);
}elseif(obj.attachEvent){
obj.attachEvent('on'+type,fn);
}
}
functionremoveEvent(obj,type,fn){//移除事件处理程序兼容;
if(obj.removeEventListener){
obj.removeEventListener(type,fn);
}esleif(obj.detachEvent){
obj.detachEvent('on'+type,fn);
}
}
functiongetTarget(evt){//得到事件目标;
if(evt.target){
returnevt.target;
}elseif(window.event.srcEleemnt){
returnwindow.event.srcElement;
}
}
四事件对象补充
1.relatedTarget
//这个属性可以在mouseover和mouseout事件中获取从哪里移入和从哪里移出的DOM对象;
box.onmouseover=function(evt){//鼠标移入box;
alert(evt.relatedTarget);//获取移入box之前的那个元素;
}
box.onmouseout=function(evt){//鼠标移出box;
alert(evt.relatedTarget);//获取移出box之后到的那个元素;
}
//IE提供了两组与之对应的属性:fromElement和toElement;
//兼容函数
functiongetEarget(evt){
vare=evt||window.event;//得到事件对象;
if(e.srcElement){//如果支持srcElement,表示IE;
if(e.type=='mouseover'){//如果是over事件;
returne.fromeElement;//就使用from;
}elseif(e.type=='mouseout'){//如果是out;
returne.toElement;//就使用to;
}
}elseif(e.relatedTarget){//如果支持relatedTarget,表示W3C;
returne.relatedTarget;
}
}
2.阻止事件的默认行为
//一个超链接的默认行为就点击然后跳转到指定的页面;
//那么阻止默认行为就可以屏蔽跳转的这种操作,而实现自定义操作;
//取消事件默认行为还有一种不规范的做法,就是返回false;
link.onclick=function(){
alert('Lee');
returnfalse;//直接返回false,就不会跳转了;
}
//PS:虽然returnfalse;可以实现这个功能,但有漏洞;
//第一:代码必须写到最后,这样导致中间的代码执行后,有可能执行不到returnfalse;
//第二:returnfalse写到最前那么之后的自定义操作就失败了;
//解决方案:在最前面就阻止默认行为,并且后面还能执行代码;
functionpreDef(evt){//跨浏览器兼容阻止默认行为;
vare=evt||window.event;
if(e.preventDefault){
e.preventDefault();//W3C,阻止默认行为;
}else{
e.returnValue=false;//IE,阻止默认行为;
}
}
3.上下文菜单事件contextmenu
//当我们右击网页的时候,会自动出现windows自带的菜单;
//那么我们可以使用contextmenu事件来修改我们指定的菜单;但前提是把右击的默认行为取消;
addEvent(window,'load',function(){
vartext=docuemnt.getElementById('text');
addEvent(text,'contextmenu',function(evt){//添加右键菜单事件处理程序;
vare=evt||window.event;
preDef(e);//阻止默认行为函数;
varmenu=document.getElementById('menu');//找到自定义的menu对象;
menu.style.left=e.clientX+'px';//确定自定义menu在屏幕上的位置;
menu.style.top=e.clientX+'px';
menu.style.visibility='visible';//设置自定义menu的属性为可见;
addEvent(document,'click',function(){//给document添加单击事件处理程序;
docuemnt.getElementById('myMenu').style.visibility='hidden';//将自定义的menu隐藏;
});
});
});
4.卸载前事件beforeunload
//这个事件可以帮助在离开本页的时候给出相应的提示;"离开"或"返回"操作;
addEvent(window.'beforeunload',function(evt){
varevt=event||window.event;
varmessage='是否离开此页?';
evt.returnValue=message;
returnmessage;
});
5.鼠标滚轮(mousewheel)和DOMMouseScroll
//用于获取鼠标上下滚轮的距离;
addEvent(docuemnt,'mousewheel',function(evt){//非Firefox;
alert(getWD(evt));
});
addEvent(docuemnt,'DOMMouseScroll',function(evt){//Firefox;
alert(getWD(evt));
});
functiongetWD(evt){
vare=evt||window.event;
if(e.wheelDelta){//mousewheel事件的滚动值保存在wheelDelta里;
returne.wheelDelta;
}elseif(e.detail){//DOMMouseScroll事件的滚动值保存在detail里;
return-evt.detail*30;//保持计算的统一;
}
}