当前位置: 移动技术网 > IT编程>开发语言>JavaScript > javascript事件绑定学习要点

javascript事件绑定学习要点

2019年07月31日  | 移动技术网IT编程  | 我要评论
事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(dom2级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。 一 传统事件绑定的问

事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(dom2级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。

一 传统事件绑定的问题

传统事件绑定中的内联模型不做讨论,基本很少去用。先来看一下脚本模型,脚本模型将一个函数赋值给一个事件处理函数。传统绑定如:

window.onload=function(){
 var box=document.getelementbyid('box');
 box.onclick = function(){
  alert('lee');
 };
};



问题一:一个事件处理函数触发两次事件

如果一个页面有两个或者多个js,并且第一个js是第一个程序开发的,第二个js是第二个程序员开发的。第一个window.onload被覆盖了,如

window.onload=function(){
 alert('lee');
};

window.onload=function(){
 alert('mr.lee');
} 

结果只是打印了 mr.lee

其实是有办法解决这个问题的,看下面这两种形式。
a:

alert(window.onload);//一开始没有注册window.onload,那么就是null

window.onload=function(){
 alert('lee');
};

alert(window.onload);//如果已经有window.onload,打印的是函数function

window.onload=function(){
 alert('mr.lee');
} 

b:

alert(typeof window.onload);//一开始没有window.onolad,旧版火狐显示undefined,新版显示object,

window.onload=function(){
 alert('lee');
};

alert(typeof window.onload);//如果已经有window.onload,所有浏览器都会显示function

window.onload=function(){
 alert('mr.lee');
}

所以解决办法有了。

 window.onload=function(){
 alert('lee');
};

if(typeof window.onload=='function'){
 var saved=null;//保存上一个事件对象
 saved=window.onload;
}

//saved 就是window.onload,saved()相当于window.onload(),但是window.onload()不能执行的
//所以saved()相当于window.onload=function(){}

window.onload=function(){
 if(saved){
  saved();//执行上一个事件 window.onload=function(){}
 }
 alert('mr.lee'); //执行本事件
}

问题二:事件切换器
切换一个id为box的div,让里面的背景red与blue直接切换,并且切换之前弹框一次,如:

window.onload=function(){
 var box=document.getelementbyid('box');
 box.classname="red";
 box.onclick=function(){
  alert('lee'); //只执行了一次
  blue.call(this);//通过匿名函数执行某一函数,那么里面的this就是代表的window,所以可以通过call传递
 };
}

function blue(){
 this.classname="blue";
 this.onclick=red;
 
}

function red(){
 this.classname="red";
 this.onclick=blue;
}

上面的代码虽然实现了切换功能,但是弹框只执行了一次。

//添加事件函数
//obj相当于window
//type相当于onload
//fn相当于function(){}
function addevent(obj,type,fn){
 //用于保存上一个事件
 var saved=null;
 if(typeof obj['on'+type]=='function'){
  saved=obj['on'+type];//保存上一个事件
 }
 obj['on'+type]=function(){
  if(saved){
   saved();
  }
  fn.call(this);
 }
 
}
addevent(window,'load',function(){
 var box=document.getelementbyid("box");
 //addevent(box,'click',function(){ //目的达到,每次都执行了,没有被覆盖
 // alert('ss');
 //});
 addevent(box,'click',blue);
});

function red(){
 this.classname="red";
 addevent(box,'click',blue);
}

function blue(){
 this.classname="blue";
 addevent(box,'click',red);
}

//当不停的切换的时候,浏览器突然卡死,并且报错:too much recursion,太多的递归
//因为积累了太多的保存的事件
//解决方案,就是用完的事件,就立刻移除掉

按照上面的代码出现了注释中的错误,解决的办法如下:

//添加事件函数
//obj相当于window
//type相当于onload
//fn相当于function(){}
function addevent(obj,type,fn){
 //用于保存上一个事件
 var saved=null;
 if(typeof obj['on'+type]=='function'){
  saved=obj['on'+type];//保存上一个事件
 }
 obj['on'+type]=function(){
  if(saved){
   saved();
  }
  fn.call(this);
 }
 
}


//当不停的切换的时候,浏览器突然卡死,并且报错:too much recursion,太多的递归
//因为积累了太多的保存的事件
//解决方案,就是用完的事件,就立刻移除掉


//移除事件函数
function removeevent(obj,type){
 if(obj['on'+type]){
  obj['on'+type]=null;
 }
}


addevent(window,'load',function(){
 var box=document.getelementbyid("box");
 //addevent(box,'click',function(){ //目的达到,每次都执行了,没有被覆盖
 // alert('ss');
 //});
 addevent(box,'click',blue);
});

function red(){
 this.classname="red";
 removeevent(this,'click');
 addevent(box,'click',blue);
}

function blue(){
 this.classname="blue";
 removeevent(this,'click');
 addevent(box,'click',red);
}

二 w3c事件处理函数
addeventlistener()与removeeventlistener()
w3c事件处理函数两个,addeventlistener()与removeeventlistener()。

//w3c自带的两个添加事件和删除事件
1.覆盖问题,解决

window.addeventlistener('load',function(){
 alert('lee');
},false);

window.addeventlistener('load',function(){
 alert('mr.lee');
},false);

window.addeventlistener('load',function(){
 alert('mrs.lee');
},false);

2.相同函数屏蔽的问题,解决

window.addeventlistener('load',init,false);
window.addeventlistener('load',init,false);
window.addeventlistener('load',init,false);
function init(){
 alert('lee');
}

3.是否可以传递this,解决
例子1:

window.addeventlistener('load',function(){
 var box=document.getelementbyid('box');
 box.addeventlistener('click',function(){
  alert(this);
 },false);
},false);

例子2:

window.addeventlistener('load',function(){
 var box=document.getelementbyid('box');
 box.addeventlistener('click',blue,false);
},false);

function red(){
 this.classname="red";
 this.removeeventlistener('click',red,false);
 this.addeventlistener('click',blue,false);
}

function blue(){
 this.classname="blue";
 this.removeeventlistener('click',blue,false);
 this.addeventlistener('click',red,false);
}

4.添加一个额外的方法,会不会被覆盖,或者只能执行一次,解决

window.addeventlistener('load',function(){
 var box=document.getelementbyid('box');
 box.addeventlistener('click',function(){
  alert('lee');
 },false);
 box.addeventlistener('click',blue,false);
},false);

综上所述:w3c是比较完美的解决了这些问题,非常好用,但是ie8和之前的浏览器并不支持,而是采用了自己的事件,当然ie9已经完全支持了w3c的这两个事件处理函数。

w3c可以设置冒泡和捕获方式。

支持w3c标准的浏览器在添加事件时用addeventlistener(event,fn,usecapture)方法,基中第3个参数usecapture是一个boolean值,用来设置事件是在事件捕获时执行,还是事件冒泡时执行。而不兼容w3c的浏览器(ie)用attachevent()方法,此方法没有相关设置,不过ie的事件模型默认是在事件冒泡时执行的,也就是在usecapture等于false的时候执行,所以把在处理事件时把usecapture设置为false是比较安全,也实现兼容浏览器的效果。

事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)。
事件冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签。
事件的传播是可以阻止的:
在w3c中,使用stoppropagation()方法
在ie下设置cancelbubble = true;

三.ie事件处理函数

attachevent()和detachevent()
ie实现了与dom中类似的两个方法:attachevent()和detachevent()。这两个方法接受相同的参数:事件名称和函数。

在使用这两组函数的时候,先把区别说一下:1.ie不支持捕获,只支持冒泡;2.ie添加事件不能屏蔽重复的函数;3.ie中的this指向的是window而不是dom对象。4.在传统事件上,ie是无法接受到event对象的,但使用了attchevent却可以,但有些区别。

1.覆盖问题,解决了,但有不同,结果是mrs.lee,mr.lee,最后是lee

window.attachevent('onload',function(){
 alert('lee');
});

window.attachevent('onload',function(){
 alert('mr.lee');
});
window.attachevent('onload',function(){
 alert('mrs.lee');
});

2.相同函数屏蔽的问题,未解决。

window.attachevent('onload',init);
window.attachevent('onload',init);

function init(){
 alert('lee');
}

3.是否可以传递this,不能,this指的是window。需要用call方法。

window.attachevent('onload',function(){
 var box=document.getelementbyid('box');
 box.attachevent('onclick',function(){
  //alert(this===box);
  alert(this===window); //true
 });
});

下面还有办法就是通过window.event.srcelement。代码如下:

window.attachevent('onload',function(){
 var box=document.getelementbyid('box');
 box.attachevent('onclick',blue);
});

function red(){
 var that=window.event.srcelement;
 that.classname="red";
 that.detachevent('onclick',red);
 that.attachevent('onclick',blue);
}

function blue(){
 var that=window.event.srcelement;
 that.classname="blue";
 that.detachevent('onclick',blue);
 that.attachevent('onclick',red);
}

4.添加一个额外的方法,会不会被覆盖,或者只能执行一次,解决。

在传统绑定上,ie是无法像w3c那样通过传参接受event对象,但是使用attachevent()却可以。

 window.attachevent('onload',function(){
 var box=document.getelementbyid('box');
 box.onclick=function(evt){ //传统方法ie无法通过参数获取evt
  alert(evt);//undefined
 }
 box.attachevent('onclick',function(evt){
  alert(evt);//object
  alert(evt.type);//click
  alert(evt.srcelement.tagname);//div
  alert(window.event.srcelement.tagname);//div
 });
});

跨浏览器的兼容

跨浏览器添加事件

function addevent(obj,type,fn){
 if(obj.addeventlistener){
  obj.addeventlistener(type,fn,false);
 }else if(obj.attachevent){
  obj.attachevent('on'+type,fn);
 }
}

跨浏览器移除事件

function removeevent(obj,type,fn){
 if(obj.removeeventlistener){
  obj.removeeventlistener(type,fn,false);
 }else if(obj.detachevent){
  obj.detachevent('on'+type,fn);
 }
}

跨浏览器获取目标对象

function gettarget(evt){
 if(evt.target){
  return evt.target;
 }else if(window.event.srcelement){
  return window.event.srcelement;
 }
}

调用方式:

addevent(window,'load',function(){
 var box=document.getelementbyid('box');
 addevent(box,'click',blue);
});


function red(evt){
 var that=gettarget(evt);
 that.classname="red";
 removeevent(that,'click',red);
 addevent(that,'click',blue);
}

function blue(evt){
 var that=gettarget(evt);
 that.classname="blue";
 removeevent(that,'click',blue);
 addevent(that,'click',red);
}

四.事件对象的其他补充

relatedtarget事件

w3c中的一个relatedtarget事件。
例如:

addevent(window,'load',function(){
 var box=document.getelementbyid('box');
 addevent(box,'mouseover',function(evt){
  alert(evt.relatedtarget); //得到移入box最近的那个dom对象
 });
 
 addevent(box,'mouseout',function(evt){
  alert(evt.relatedtarget); //从box移出最近的那个dom对象
 });
});

ie提供了两组分别用于移入移出的属性fromelement和toelement,分别对应mouseover和mouseout。

addevent(window,'load',function(){
 var box=document.getelementbyid('box');
 addevent(box,'mouseover',function(){
  alert(window.event.fromelement.tagname); //得到移入box最近的那个dom对象
 });
 
 addevent(box,'mouseout',function(){
  alert(window.event.toelement.tagname); //从box移出最近的那个dom对象
 });
});

ps:fromelement和toelement如果分别对应相反的鼠标事件,没有任何意义。

剩下要做的就是跨浏览器兼容操作:

function gettarget(evt){
 var e=evt || window.event;
 if(e.srcelment){ //ie
  if(e.type=='mouseover'){
   return e.fromelement.tagname;
  }else if(e.type="mouseout"){
   return e.toelement.tagname;
  }
 }else if(e.relatedtarget){ //w3c
  return e.relatedtarget;
 }
}

屏蔽跳转操作

取消事件的默认行为有一种不规范的做法,就是返回false。

link.onclick=function(){
 alert('lee');
 return false;
}

ps:虽然return false;可以实现这个功能,但是有漏洞。
第一:必须写到最后,这样导致中奖的代码执行后,有可能执行不到return false;
第二:return false 写到最前那么之后的自定义操作就失效了。
所以最好的办法应该是在最前面就阻止默认行为,并且后面的代码还可以执行。

link.onclick=function(evt){
 evt.preventdefault;//w3c,阻止默认行为
 alert('lee');
}

link.onclick=function(evt){
 window.event.returnvalue=false;//ie,阻止默认行为
 alert('lee');
}

那么跨浏览器的兼容:

function predef(evt){
 var e=evt || window.event;
 if(e.preventdefault){
  e.preventdefault();
 }else{
  e.returnvalue=false;
 }
}

右键菜单contextmenu
兼容:

function predef(evt){
 var e=evt || window.event;
 if(e.preventdefault){
  e.preventdefault();
 }else{
  e.returnvalue=false;
 }
}

addevent(window,"load",function(){
 var body=document.getelementsbytagname('body')[0];
 addevent(body,'contextmenu',function(evt){
  predef(evt);
 })
});

ps:contextmenu事件很常用,这直接导致浏览器兼容性较为稳定。

卸载前事件:beforeunload
这个事件可以帮助在离开本页的时候给出相应的提示,“离开”或者“返回”操作。

addevent(window,'beforeonload',function(){
 predef(evt);
});

鼠标滚轮(mousewheel)和dommousescroll
用于获取鼠标上下滚轮的距离

addevent(document,'mousewheel',function(evt){ //非火狐
 alert(getwd(evt));
});

addevent(document,'dommousescroll',function(evt){ //火狐
 alert(getwd(evt));
});

function getwd(evt){
 var e=evt|| window.event;
 if(e.wheeldelta){
  return e.wheeldelta;
 }else if(e.detail){ //火狐
  return -evt.detail*30;
 }
}

ps:通过浏览器检测可以确定火狐只执行dommousescroll。

domcontentloaded事件和readystatechange事件

domcontentloaded事件和readystatechange事件,有关dom加载方面的事件。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网