這篇文章主要介紹了javascript事件綁定學(xué)習(xí)要點,主要包含下面四個方面1.傳統(tǒng)事件綁定的問題,2.W3C事件處理函數(shù),3.IE事件處理函數(shù),4.事件對象的其他補充,有需要的小伙伴可以參考下
事件綁定分為兩種:一種是傳統(tǒng)事件綁定(內(nèi)聯(lián)模型,腳本模型),一種是現(xiàn)代事件綁定(DOM2級模型)?,F(xiàn)代事件綁定在傳統(tǒng)綁定上提供了更強大更方便的功能。
一 傳統(tǒng)事件綁定的問題
傳統(tǒng)事件綁定中的內(nèi)聯(lián)模型不做討論,基本很少去用。先來看一下腳本模型,腳本模型將一個函數(shù)賦值給一個事件處理函數(shù)。傳統(tǒng)綁定如:
window.onload=function(){
var box=document.getElementById('box');
box.onclick = function(){
alert('Lee');
};
};
問題一:一個事件處理函數(shù)觸發(fā)兩次事件
如果一個頁面有兩個或者多個js,并且第一個js是第一個程序開發(fā)的,第二個js是第二個程序員開發(fā)的。第一個window.onload被覆蓋了,如
window.onload=function(){
alert('Lee');
};
window.onload=function(){
alert('Mr.lee');
}
結(jié)果只是打印了 Mr.lee
其實是有辦法解決這個問題的,看下面這兩種形式。
a:
alert(window.onload);//一開始沒有注冊window.onload,那么就是null
window.onload=function(){
alert('Lee');
};
alert(window.onload);//如果已經(jīng)有window.onload,打印的是函數(shù)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);//如果已經(jīng)有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()相當(dāng)于window.onload(),但是window.onload()不能執(zhí)行的
//所以saved()相當(dāng)于window.onload=function(){}
window.onload=function(){
if(saved){
saved();//執(zhí)行上一個事件 window.onload=function(){}
}
alert('Mr.lee'); //執(zhí)行本事件
}
問題二:事件切換器
切換一個id為box的div,讓里面的背景red與blue直接切換,并且切換之前彈框一次,如:
window.onload=function(){
var box=document.getElementById('box');
box.className="red";
box.onclick=function(){
alert('Lee'); //只執(zhí)行了一次
blue.call(this);//通過匿名函數(shù)執(zhí)行某一函數(shù),那么里面的this就是代表的window,所以可以通過call傳遞
};
}
function blue(){
this.className="blue";
this.onclick=red;
}
function red(){
this.className="red";
this.onclick=blue;
}
上面的代碼雖然實現(xiàn)了切換功能,但是彈框只執(zhí)行了一次。
//添加事件函數(shù)
//obj相當(dāng)于window
//type相當(dāng)于onload
//fn相當(dāng)于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(){ //目的達(dá)到,每次都執(zhí)行了,沒有被覆蓋
// alert('ss');
//});
addEvent(box,'click',blue);
});
function red(){
this.className="red";
addEvent(box,'click',blue);
}
function blue(){
this.className="blue";
addEvent(box,'click',red);
}
//當(dāng)不停的切換的時候,瀏覽器突然卡死,并且報錯:too much recursion,太多的遞歸
//因為積累了太多的保存的事件
//解決方案,就是用完的事件,就立刻移除掉
按照上面的代碼出現(xiàn)了注釋中的錯誤,解決的辦法如下:
//添加事件函數(shù)
//obj相當(dāng)于window
//type相當(dāng)于onload
//fn相當(dāng)于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);
}
}
//當(dāng)不停的切換的時候,瀏覽器突然卡死,并且報錯:too much recursion,太多的遞歸
//因為積累了太多的保存的事件
//解決方案,就是用完的事件,就立刻移除掉
//移除事件函數(shù)
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(){ //目的達(dá)到,每次都執(zhí)行了,沒有被覆蓋
// 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事件處理函數(shù)
addEventListener()與removeEventListener()
W3C事件處理函數(shù)兩個,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.相同函數(shù)屏蔽的問題,解決
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.添加一個額外的方法,會不會被覆蓋,或者只能執(zhí)行一次,解決
window.addEventListener('load',function(){
var box=document.getElementById('box');
box.addEventListener('click',function(){
alert('Lee');
},false);
box.addEventListener('click',blue,false);
},false);
綜上所述:W3C是比較完美的解決了這些問題,非常好用,但是IE8和之前的瀏覽器并不支持,而是采用了自己的事件,當(dāng)然IE9已經(jīng)完全支持了W3C的這兩個事件處理函數(shù)。
W3C可以設(shè)置冒泡和捕獲方式。
支持W3C標(biāo)準(zhǔn)的瀏覽器在添加事件時用addEventListener(event,fn,useCapture)方法,基中第3個參數(shù)useCapture是一個Boolean值,用來設(shè)置事件是在事件捕獲時執(zhí)行,還是事件冒泡時執(zhí)行。而不兼容W3C的瀏覽器(IE)用attachEvent()方法,此方法沒有相關(guān)設(shè)置,不過IE的事件模型默認(rèn)是在事件冒泡時執(zhí)行的,也就是在useCapture等于false的時候執(zhí)行,所以把在處理事件時把useCapture設(shè)置為false是比較安全,也實現(xiàn)兼容瀏覽器的效果。
事件捕獲階段:事件從最上一級標(biāo)簽開始往下查找,直到捕獲到事件目標(biāo)(target)。
事件冒泡階段:事件從事件目標(biāo)(target)開始,往上冒泡直到頁面的最上一級標(biāo)簽。
事件的傳播是可以阻止的:
在W3c中,使用stopPropagation()方法
在IE下設(shè)置cancelBubble = true;
三.IE事件處理函數(shù)
attachEvent()和detachEvent()
IE實現(xiàn)了與DOM中類似的兩個方法:attachEvent()和detachEvent()。這兩個方法接受相同的參數(shù):事件名稱和函數(shù)。
在使用這兩組函數(shù)的時候,先把區(qū)別說一下:1.IE不支持捕獲,只支持冒泡;2.IE添加事件不能屏蔽重復(fù)的函數(shù);3.IE中的this指向的是window而不是DOM對象。4.在傳統(tǒng)事件上,IE是無法接受到event對象的,但使用了attchEvent卻可以,但有些區(qū)別。
1.覆蓋問題,解決了,但有不同,結(jié)果是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.相同函數(shù)屏蔽的問題,未解決。
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.添加一個額外的方法,會不會被覆蓋,或者只能執(zhí)行一次,解決。
在傳統(tǒng)綁定上,IE是無法像W3C那樣通過傳參接受event對象,但是使用attachEvent()卻可以。
window.attachEvent('onload',function(){
var box=document.getElementById('box');
box.onclick=function(evt){ //傳統(tǒng)方法IE無法通過參數(shù)獲取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);
}
}
跨瀏覽器獲取目標(biāo)對象
function getTarget(evt){
if(evt.target){
return evt.target;
}else if(window.event.srcElement){
return window.event.srcElement;
}
}
調(diào)用方式:
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,分別對應(yīng)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如果分別對應(yīng)相反的鼠標(biāo)事件,沒有任何意義。
剩下要做的就是跨瀏覽器兼容操作:
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;
}
}
屏蔽跳轉(zhuǎn)操作
取消事件的默認(rèn)行為有一種不規(guī)范的做法,就是返回false。
link.onclick=function(){
alert('Lee');
return false;
}
PS:雖然return false;可以實現(xiàn)這個功能,但是有漏洞。
第一:必須寫到最后,這樣導(dǎo)致中獎的代碼執(zhí)行后,有可能執(zhí)行不到return false;
第二:return false 寫到最前那么之后的自定義操作就失效了。
所以最好的辦法應(yīng)該是在最前面就阻止默認(rèn)行為,并且后面的代碼還可以執(zhí)行。
link.onclick=function(evt){
evt.preventDefault;//w3c,阻止默認(rèn)行為
alert('Lee');
}
link.onclick=function(evt){
window.event.returnValue=false;//IE,阻止默認(rèn)行為
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事件很常用,這直接導(dǎo)致瀏覽器兼容性較為穩(wěn)定。
卸載前事件:beforeunload
這個事件可以幫助在離開本頁的時候給出相應(yīng)的提示,“離開”或者“返回”操作。
addEvent(window,'beforeonload',function(){
preDef(evt);
});
鼠標(biāo)滾輪(mousewheel)和DOMMouseScroll
用于獲取鼠標(biāo)上下滾輪的距離
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:通過瀏覽器檢測可以確定火狐只執(zhí)行DOMMouseScroll。
DOMContentLoaded事件和readystatechange事件
DOMContentLoaded事件和readystatechange事件,有關(guān)DOM加載方面的事件。