javascript事件模型與事件監聽器【知識總結】


    一直想寫一篇關於事件和監聽器的文章,但又怕顯得很多餘,畢竟這不是什麼新知識,而且網上的優秀文章已經很多了。總也有這樣的感覺:這部分知識我似乎已經懂了,但有時候也會遇到些問題,雖然可以不求甚解的換個方法解決了問題,但總覺得不對勁,肯定還有哪些地方我沒有理解到位。本着對前端工作的嚴謹態度,我看還是有必要重新再整理一遍。一是自己可以再溫習一遍,二是算寫給剛剛入門的新手看吧,希望可以爲你們帶來幫助。

    似乎是在很久很久以前,我們就已經接觸監聽器了,從剛剛開始接觸HTML的時候,我們就已經會寫onclick了,後來就接觸了jquery,知道了還可以這麼寫$().click()、$().bind()、$.live()。然後在很長一段的時間裏,我們就自認爲已經掌握了這部分知識,樂此不疲的在項目中時而bind(),時而live(),直到出了問題,才反應過來。javascript事件模型與監聽器,似乎沒那麼簡單呀,此事必有蹊蹺!所以在此重新將這些知識過一遍。


一、javascript事件模型

    什麼是事件呢?這還用問,就是鼠標點擊啦、移動啦、鍵盤按下啦等等一系列用戶觸發的動作。我們可以用相應的事件類型表示他們,click、mousemove、keydown。。。那這有什麼蹊蹺呢?值得一提的便是一次事件處理的過程:捕獲-冒泡機制。

    舉個例子在說明:HTML代碼如下

<style type="text/css">
li{width:100px;background-color:red;}
p{width:50px; background-color:green;}
</style>
<ul>
<li><p>0</p></li>
</ul>
js代碼如下:

$('li').click(function(){	
    alert('li');
});
		
$('p').click(function(event){
    lert('p');
});
    當我們點擊到li元素時,會彈出“li",這個沒錯。但當我們點擊p元素時,會發現先彈出"p",接着又彈出"li"。不怕笑話,之前我是這麼理解的:因爲點擊p的時候同時點到li了,就像踩到地上一張紙,同時也踩到地了一樣。事實上這麼理解是一個錯誤。真正的原因正是事件的冒泡機制,所以正確的解釋是:發生在p上的點擊事件會冒泡到li,因爲li上也綁定了點擊事件監聽器,捕獲到這一事件,所以也執行了它自己的監聽函數。

    那麼如何阻止這種情況呢(因爲有時候我們並不想讓它冒泡),很簡單,如下代碼:

$('p').click(function(event){
	event.stopPropagation(); //阻止事件冒泡
	alert('p'+$(this).index());
});
    這下你再點擊p的時候,就不會再彈li了。

    下面是捕獲-冒泡的示意圖,當點擊了p的時候,javascript引擎會從文檔的根節點開始,層層深入,找到觸發事件的那個元素,這個過程是捕獲,在捕獲的過程中,會依次檢查每個節點上是否綁定了相應的監聽器,如果有,則執行其監聽函數,然後事件還會再向上一層一層冒泡,知道文檔根節點,這個過程,叫做冒泡,同樣,冒泡到的每個節點都檢查一遍是否綁定了監聽器。原來一次事件是這麼複雜的過程。明白了這個過程,將有利於我們處理以後的一些問題。



二、綁定事件監聽器的方法彙總

    綁定事件監聽器,也叫註冊監聽器,一回事。就是我們預先定義好的處理某個元素被觸發某個事件的函數。方式有如下幾種:

爲了方便我先提前定義一個函數f:

function f(){
    alert(1);
}
var p = document.getElementsByTagname('p')[0];

1. 直接寫在元素中,作爲元素的一個屬性,如:<p onclick="f()">11</p>

2. 傳統javascript綁定方式,如:p.onclick = f;  //注意這裏是沒有括號的哦,與上面不同

3. w3c指定的標準方法:p.addEventListener('click',f,false);    //三個參數分別是:事件類型、處理函數、是否在捕獲階段處理

4.IE瀏覽器不支持第三種,在IE下的寫法:p.attachEvent('onclick',f);  //注意事件類型前面要加on

5.jquery的寫法有如下幾種:

$('p').bind('click',f);    //綁定到具體的這個元素

$('p').click(f);  //上面寫法的簡寫,僅此而已

$('p').live('click',f);  //將監聽器綁定到document對象上,等事件冒泡的時候再處理

$('li').delegate('p','click',f);  //可以指定將監聽器綁定到那個對象上,不一定是document

live()和delegate()都是使用了事件委託的機制,沒有將監聽器綁定在具體的某一個元素上,而是綁定在他們的父級節點上。兩者都調用了jquery的一個底層一點的方法on(),jquery推薦以後用on()取代掉這兩者,原因是使用live()的時候當dom層級較多,會引發一些意外的問題。而且是jquery1.9版本之後也將不再支持live()方法,所以我們以後就用on()來統一使用就可以。

三、理解委託機制,正確使用on()

    上面提到了委託機制,到底是個什麼情況呢?委託,就是本來自己乾的事情拜託別人幫忙幹了,本來自己要挨個給元素綁定監聽器,現在可以把這個任務交給這些元素的父親,讓他來完成。這下自己就可以省事了,而且呢,當父親又添了孩子,他自己知道該如何處理,這就是我們可以使用委託機制來爲動態生成的節點綁定監聽器。

    既然用on可以實現bind和live,那不妨就在項目中統一了,就用on()來綁定監聽器。所以要明白到底如何代替兩者呢。先看on()函數的參數,

on(events,[selector],[data],fn)

分別表示:事件類型、要監聽的元素、傳給監聽函數的參數,處理函數

用法如下:

    $('p').on('click',f); //這是代替bind的寫法,直接綁定在了<p>元素上

    $('li').on('click','p',f); //這是代替delegate的寫法,是指將監聽器綁定在<li>元素上,委託它來處理它的子元素的點擊事件。

    這樣,我們便可以輕鬆放下live()了。

    那具體什麼情況下用哪種方式呢?我想這並沒有絕對的答案,只要你明白了這兩種方式的區別,結合你代碼中的情況,便可以給出正確做法。例如,你的程序會通過ajax請求給一個容器中動態添加子元素,那這時候綁定到此父容器是很好的選擇。如果你的DOM層級較深,而且要監聽的元素並不是大批量的,那本直接綁在該元素上是很好的選擇。














    

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