1.傳統事件綁定的問題
2.W3C事件處理函數
3.IE事件處理函數
4.事件對象的其餘補充程序員
事件綁定分爲兩種:一種是傳統事件綁定(內聯模型,腳本模型),一種是現代事件綁定(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.leethis
實際上是有辦法解決這個問題的,看下面這兩種形式。
a:spa
alert(window.onload);//一開始沒有註冊window.onload,那麼就是null window.onload=function(){ alert('Lee'); }; alert(window.onload);//若是已經有window.onload,打印的是函數function window.onload=function(){ alert('Mr.lee'); }
b:code
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自帶的兩個添加事件和刪除事件
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實現了與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); }
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; } }
兼容:
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事件很經常使用,這直接致使瀏覽器兼容性較爲穩定。
這個事件能夠幫助在離開本頁的時候給出相應的提示,「離開」或者「返回」操做。
addEvent(window,'beforeonload',function(){ preDef(evt); });
用於獲取鼠標上下滾輪的距離
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事件,有關DOM加載方面的事件。