js中獲取事件對象的方法小結

事件對象 的獲取很簡單,很久前我們就知道IE中事件對象是作爲全局對象( window.event )存在的,Firefox中則是做爲句柄( handler )的第一個參數傳入內的。所以一行代碼就可以搞定

代碼如下:

var evt = window.event || arguments[0]; 

下面分三種添加事件的方式討論,你也許會看到以前沒有看到過的獲取方式。 
1,第一種添加事件的方式,直接在html的屬性中寫JS代碼 
複製代碼代碼如下:

<div οnclick="alert(4);">Div1 Element</div> 

大概這是上世紀90年代的寫法,那時候直接把js代碼寫在網頁中很普遍,也許那時候的js並不太重要,只是用來做做驗證或一些花哨的效果而已。如何在這種添加事件方式下獲取到事件對象?IE中很簡單,因爲event是作爲全局對象的,所以直接使用event即可,如下 
複製代碼代碼如下:

<div οnclick="alert(window.event.type);">Div1 Element</div> 

點擊該Div後,IE中會彈出'click'字符的信息框。說明事件對象獲取到了,如果在 Opera/Safari/Chrome 中也測試了,會發現效果和IE一樣,說明 Opera/Safari/Chrome 中也支持IE方式( window.event )獲取事件對象。 
Firefox中會報錯,提示:window.event is undefined,說明Firefox不支持IE方式獲取事件對象而是以句柄的第一個參數傳入的,文章開頭意見提到了。 
上面的用 window.event 來獲取事件對象,其實window可以省略的,就像使用alert而不是window.alert一樣。如 
複製代碼代碼如下:

<div οnclick="alert(event.type);">Div1 Element</div> 

在 IE/Opera/Safari/Chrome 中測試,和剛剛不會有什麼區別。在Firefox中再測,會有個驚喜,你會發現居然彈出的是"click"信息框,而不是"undefined"。 
兩次測試區別僅僅一個用window.event.type,一個用event.type。這個問題下面詳細討論。 
下面用句柄第一個參數來獲取事件對象,可以把onclick屬性的值想象成一個匿名函數,onclick屬性值的字符串實際上都是這個匿名函數內的js代碼。 
既然這樣,我們就可以通過Function的一個屬性argumengs獲取到該匿名函數的第一個參數,而該參數就是事件對象。如 
複製代碼代碼如下:

<div οnclick="alert(arguments[0].type);">Div1 Element</div> 

IE中會報錯,提示:arguments.0.type爲空或不是對象 
Firefox/Opera/Safari/Chrome 中會彈出"click"內容的信息框,說明他們都支持事件對象作爲句柄第一個參數傳入。從側面也說明了 Opera/Safari/Chrome 不僅支持W3C標準方式獲取事件對象,同時也兼容了IE方式獲取事件對象。 
既然知道onclick對應的是一個匿名函數,我們不妨把該匿名函數打印出來看看,只需以下代碼 
複製代碼代碼如下:

<div οnclick="alert(arguments.callee);">Div1 Element</div> 

在各瀏覽器中點擊該Div,結果如下:

IE6/7/8 :

function onclick(){ alert(arguments.callee);}

IE9 :

function onclick(event){ alert(arguments.callee);}

Firefox / Safari :
function onclick(event) { alert(arguments.callee);}

Chrome : 
function onclick(evt) { alert(arguments.callee);}

Opera :

function anonymous(event) {alert(arguments.callee);}

觀察這些函數發現:
IE6/7/8沒有定義參數
IE9/Firefox/Safari/Opera 定義了參數event
Chrome定義了參數evt。
現在回到上面遺留的問題,如下

複製代碼代碼如下:

<div οnclick="alert(window.event.type);">Div1 Element</div> 
<div οnclick="alert(event.type);">Div1 Element</div> 

這兩個div的區別僅window.event.type和event.type。分別點擊後,後者在Firefox中不彈出"undefined",而是"click",是因爲Firefox中匿名函數定義了參數event,該參數剛好與IE的全局對象event同名,從而誤以爲Firefox也支持IE方式獲取事件對象。 

同樣的道理,Chrome中定義的參數是evt,那麼在Chrome中還可以通過以下方式獲取事件對象,如下 
複製代碼代碼如下:

<div οnclick="alert(evt);">Div1 Element</div> 

2,第二種添加事件的方式,定義一個函數,賦值給html元素的onXXX屬性 
複製代碼代碼如下:

<script type="text/javascript"> 
function clk(){} 
</script> 
<div οnclick="clk()">Div2 Element</div> 

先定義函數clk,然後賦值給onclick屬性,這種方式也應該屬於上世紀90年代的流行寫法。比第一種方式好的是它把業務邏輯代碼都封裝在一個函數裏了,使HTML代碼與JS代碼稍微有點兒分離,不至於第一種那麼緊密耦合。 
如何在這種方式(clk函數內)中獲取事件對象?IE中使用全局對象event仍然沒問題,如: 
複製代碼代碼如下:

<script type="text/javascript"> 
function clk(){alert(window.event);} 
</script> 
<div οnclick="clk()">Div2 Element</div> 

點擊Div後,除Firefox外,IE/Opera/Safari/Chrome都能正常獲取事件對象。上面已經提到了 Opera/Safari/Chrome 兼容IE方式(window.event)獲取事件對象,而唯獨Firefox不支持。從而Firefox中只能通過參數傳入了。試着這麼寫 
複製代碼代碼如下:

<script type="text/javascript"> 
function clk(){alert(event);} 
</script> 
<div οnclick="clk()">Div2 Element</div> 

因爲在Firefox中匿名函數是具有event參數的,而clk()是在匿名函數之內的,打印出匿名函數便知 
複製代碼代碼如下:

<script type="text/javascript"> 
function clk(){alert(arguments.callee.caller);} 
</script> 
<div οnclick="clk()">Div2 Element</div> 

點擊該Div,Firefox彈出信息框內容如下 
複製代碼代碼如下:

function onclick(event) { 
clk(); 


回到clk中的alert(event),既然匿名函數的event傳入了,那麼在該閉包中clk是可以獲取到event的,事實上點擊後Firefox會報錯:event is not defined。猜測該匿名函數的閉包和function clk(){alert(event);}不是同一個閉包環境。這種方式不行,則只能通過顯示的參數傳入了,如 
複製代碼代碼如下:

<script type="text/javascript"> 
function clk(e){alert(e);} 
</script> 
<div οnclick="clk(arguments[0])">Div2 Element</div> 

點擊Div,在Firefox中正確彈出了事件對象,支持參數傳入的瀏覽器都可以,如Opera/Safari/Chrome。 
把以上代碼中的arguments[0]改成event,那麼所有瀏覽器都支持。 
把以上代碼中的arguments[0]改成window.event,那麼將只有Firefox不支持。 
把以上代碼中的arguments[0]改成evt,那麼將只有Chrome支持。 
思考下爲什麼? 
3,第三種添加事件方式,使用element.onXXX方式 
複製代碼代碼如下:

<div id="d3">Div3 Element</div> 
<script type="text/javascript"> 
var d3 = document.getElementById('d3'); 
d3.onclick = function(){ } 
</script> 

這種方式也比較早期,但好處是可以將JS與HTML完全分離,但前提是需要給HTML元素提供一個額外的id屬性(或其它能獲取該元素對象的方式)。 

這種方式添加事件IE6/7/8只支持window.event不支持參數傳入,Firefox只支持參數傳入不支持其它方式。IE9/Opera/Safari/Chrome 兩種方式都支持。 


window.οnlοad= function(){
var d3 = document.getElementById('d3'); 
d3.onclick = function(e){alert(e); } 




4,第四種添加事件方式,使用addEventListener或IE專有的attachEvent 
複製代碼代碼如下:

<div id="d4">Div4 Element</div> 
<script type="text/javascript"> 
var d4 = document.getElementById('d4'); 
function clk(){alert(4)} 
if(d4.addEventListener){ 
d4.addEventListener('click',clk,false); 

if(d4.attachEvent){ 
d4.attachEvent('onclick',clk); 

</script> 

這是目前推薦的方式,較前兩種方式功能更爲強大,可以爲元素添加多個句柄(或稱響應函數),支持事件冒泡或捕獲,前三種方式默認都是冒泡。當然IE6/7/8仍然沒有遵循標準而使用了自己專有的attachEvent,且不支持事件捕獲。IE9 中已經支持addEventListener了。 
先用window.event測試,如 
複製代碼代碼如下:

<script type="text/javascript"> 
var d4 = document.getElementById('d4'); 
function clk(){alert(window.event)} 
if(d4.addEventListener){ 
d4.addEventListener('click',clk,false); 

if(d4.attachEvent){ 
d4.attachEvent('onclick',clk); 

</script> 

點擊Div[id=d4],IE/Opera/Safari/Chrome都正確的彈出了事件對象信息框,Firefox彈出的是"undefined",預料之中,因爲Firefox不支持window.event作爲事件對象。 
再換成句柄的第一個參數測試,如 
複製代碼代碼如下:

<script type="text/javascript"> 
var d4 = document.getElementById('d4'); 
function clk(e){alert(e)} 
if(d4.addEventListener){ 
d4.addEventListener('click',clk,false); 

if(d4.attachEvent){ 
d4.attachEvent('onclick',clk); 

</script> 

測試之前,猜測一下什麼結果,可能有人會覺得IE中應該彈出undefined,其它瀏覽器都是事件對象。事實上所有瀏覽器彈出的信息框顯示都是事件對象。 
總結下: 
1,IE6/7/8支持通過window.event獲取對象,通過attachEvent方式添加事件時也支持事件對象作爲句柄第一個參數傳入 
2,Firefox只支持事件對象作爲句柄第一個參數傳入 
3,IE9/Opera/Safari/Chrome兩種方式都支持 


關鍵字:arguments,callee,caller
arguments:表示傳入函數的參數
callee:表示函數和函數主體的語句
caller:表示調用該函數的函數
arguments

該對象代表正在執行的函數和調用它的函數的參數。
caller
返回一個對函數的引用,該函數調用了當前函數。
functionName.caller
functionName 對象是所執行函數的名稱。
說明
對於函數來說,caller屬性只有在函數執行時纔有定義。如果函數是由頂層調用的,那麼 caller包含的就是 null 。如果在字符串上下文中使用 caller 屬性,那麼結果和functionName.toString一樣,也就是說,顯示的是函數的反編譯文本。
callee

返回正被執行的 Function 對象,也就是所指定的Function 對象的正文。

[function.]arguments.callee
可選項 function 參數是當前正在執行的 Function 對象的名稱。

說明

callee 屬性的初始值就是正被執行的 Function 對象。

callee 屬性是 arguments對象的一個成員,它表示對函數對象本身的引用,這有利於匿名
函數的遞歸或者保證函數的封裝性,例如下邊示例的遞歸計算1到n的自然數之和。而該屬性
僅當相關函數正在執行時纔可用。還有需要注意的是callee擁有length屬性,這個屬性有時候
用於驗證還是比較好的。arguments.length是實參長度,arguments.callee.length是
形參長度,由此可以判斷調用時形參長度是否和實參長度一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章