Javascript 事件流和事件綁定

來源:互聯網
上載者:User
文章目錄
  • 冒泡型事件(Dubbed Bubbling)
  • 捕獲型事件(Event Capturing)
  • DOM 事件流
  • 那些事件是支援冒泡,那些不支援?
  • What is ‘this'?

事件流

瀏覽器中的事件流意味著頁面上可有不僅一個,甚至多個元素響應同一個事件。而這一個或多個元素響應事件發生的先後順序在各個瀏覽器(主要針對IE和Netscape)上是不同的。

冒泡型事件(Dubbed Bubbling)

IE上的解決方案就是冒泡型事件(Dubbed Bubbling)。冒泡型事件的基本思想是,事件按照從最特定的事件目標到最不特定的事件目標(document對象)的順序觸發。

樣本(1):點擊我觸發冒泡型事件流

樣本(1)的XHTML代碼結構:

<span id="cnt0">
<a href=”#1″ id=”link0″>點擊我觸發冒泡型事件流</a>
</span>

這個樣本裡我同時給span和a標籤綁定了click事件,大家看到了,我們點擊這個a標籤,一次點擊(click)同時觸發了兩個元素a和span的事件。先觸發的是綁定給a標籤的click事件,然後觸發的是span標籤的click事件。也就是前面提到的“頁面上可有不僅一個,甚至多個元素響應同一個事件”和“事件按照從最特定的事件目標到最不特定的事件目標(document對象)的順序觸發”。

這個樣本裡我們點擊的第一目標是a標籤這個連結,它就是前面提到的“最特定的事件目標”,然後才是span這個相對“不特定的事件目標”。再看看我的XHTML代碼結構,你會發現a標籤包含在span標籤中,也就是說span是a標籤的父節點,如果大家還不是很清晰的知道他們之間的關係,讓我們看看下面的這個DOM樹的結構圖:

事件觸發的順序是從最特定的目標,沿著DOM樹不斷的向上觸發click事件,就像氣泡從下一直上冒的過程一樣。“冒泡型”也就是這麼得來的,也很形象。這裡要說明的是,由於我只給a和span綁定了click事件,所以“冒”到span就到頂了,如果你也給包含他們的p標籤還有document綁定click事件,這個冒泡的過程就會一直延續到document的事件觸發才結束。

另外要說明的是,在IE5.5中冒泡的最高層DOM節點為document,IE6中則可以支援html節點。在Mozllia1.0及之後的版本也支援冒泡,而它則更可以冒到window視窗對象。

捕獲型事件(Event Capturing)

相對IE4.0,Netscape4.0則使用的是捕獲型事件的解決方案。這個事件觸發的過程則正好和冒泡相反——在捕獲型事件中,事件從最不精確的對象(document對象)開始觸發,然後到最精確的對象。還是前面的樣本,不過現在換由捕獲型事件觸發(當然你需要在Netscape或Firefox中測試)。

樣本(2):點擊我觸發捕獲型事件流

樣本(2)的XHTML代碼結構:

<span id="cnt1">
<a href=”#1″ id=”link1″>點擊我觸發捕獲型事件流</a>
</span>

事件觸發的循序正好跟前面的冒泡相反,這裡我就不贅述了。

DOM 事件流

這個事件流則是W3C制定一個標準規範,它同時支援兩種事件流模式,不過是先發生捕獲型事件流,再發生冒泡型事件流。

DOM事件流最獨特的是,它支援文本節點也觸發事件(IE中這不支援)。不過說實話,我現在還看不出來讓文本節點支援事件有什麼作用。

最後要說的是,根據最近大家在開發的實踐過程中的運用,我們一般都採取冒泡型的事件流觸發方式,這點我們的IE做的比較成功。至於原因,我想你可以通過上面的解釋可以看出,畢竟我們給元素觸發事件,肯定是希望從我們最希望先觸發(從最精確的)的那個開始。而DOM的先捕獲後冒泡我覺得只能用讓我很疑惑來形容我的感受。因為如果按照DOM的事件流,我們的事件要被觸發2次,而我們一般都只會選擇一個類型的事件流,值希望觸發一次,很難理解當初W3C是怎麼想的?!一個字——暈!可能我的理解能力有限,不過這是我的真是感受。

事件處理函數/監聽函數

這裡我不想做過多的介紹,我們知道在IE裡使用attachEvent(”NAME_OF_EVENT_HANDLER”, fnHandler)給元素繫結事件,而在支援DOM事件流的瀏覽器裡,則使用addEventListener(”NAME_OF_EVENT_HANDLER”, fnHandler, isCapture)。前面我控制FIREFOX中觸發捕獲事件流,就是通過設定isCapture(ture:捕獲;false:冒泡)做到的。還有就是我們傳統element.onclick或者element['on'+eventName],這個是所有瀏覽器都支援的事件綁定的監聽器,而且我測試的結果XP下的IE6~8、Firefox2.0~3.5、Safari4.0、Opera9.6.4、Chrome2.0.180都是以冒泡的事件流觸發的。更老的瀏覽器我就沒有測試過了,不過根據《Pro Javascript Techniques》裡介紹,老的瀏覽器使用onclick這樣的事件綁定,觸發的也是冒泡事件流。如果你有興趣可以淘下那些古董瀏覽器,測試一番。不過還是有不支援冒泡的事件的,下面我們就講講。

那些事件是支援冒泡,那些不支援?

這個是比較有意思的,這裡的總結都來自PPK在YAHOO的演講《Javascript Event》(推薦大家一定看看,很經典!),簡單總結PPK的內容,基本上只有onload、unload、focus、blur、submit和change事件是不支援冒泡的,這也是我在前面說“一般使用冒泡事件流”。自然向keydown、keypress、keyup、click、dbclick、mousedown、mouseout、mouseover、mouseup、mousemove。用PPK的話說,那就是“Mouse and Key Events”支援冒泡,而Interface Events(也就是《Javascript進階程式設計》裡的HTML(HTML是來構建interface的)事件。)則只支援捕獲。

他又說了下是,click是“最安全的”事件,它即支援冒泡,又支援捕獲。滑鼠事件可以觸發click,鍵盤事件也可以觸發click。還有就是在支援DOM事件的瀏覽器裡,focus和blur是只支援捕獲的,所以如果你如果用到我下面給出的addEvent函數,在給元素繫結focus或則blur事件時,bCapture一定要設定為true。那麼這裡就發生了一個問題,IE是不支援捕獲的,那不是觸發不了這連個事件?呵呵,是個嚴重的問題哦?不過在IE裡使用focus和blur事件的時候,其實觸發的是IE的focusin和focusout,當然這兩個事件也是只支援冒泡的。

PPK雖然這麼說,但是我還是想實踐一下,於是我這裡這麼處理了下,window.onfocus = function(){alert('ok')},lnkOne.onfocus = demoClick;有趣的事情發生了!在IE6裡,當你點擊我的第一個樣本連結,呵呵,視乎是即冒泡完了又捕獲了,在IE8中則只冒泡了,不過這個只是你點這個連結的時候發生的情況。接著我又算是試探性地“無意中”測試了下,點擊其他的應用程式,又一個讓我意想不到的情況發生了,居然先觸發了window.onfocus,接著觸發了lnk.onfocus!!於是我立刻測試了IE6,一樣!視乎IE也“瘋狂”了一把,觸發了onfocus的捕獲哦,哈哈!!!難道IE也支援捕獲,IE也瘋狂???!!!!還是這個現象有其他的解釋??疑惑!?!?!!呵呵,起初我確實這麼想,讓我驚喜了一把,不過仔細想了想,只是事件執行順序的原因造成了這樣的假象。

呵呵,原來在IE6裡,點選連結,先觸發了onfocus,彈出提示‘A',然後關閉彈出的提示框視窗時,視窗又獲得了焦點,又觸發了window的焦時間點事件。然後是觸發了A標籤的的click事件,然後關閉彈出的提示視窗時,又讓視窗獲得了焦點,然後又是A標籤獲得焦點。而IE8測試正確的,當觸發了click事件後,再關閉提示視窗的時候,就不在觸發window的focus。哎!空歡喜了一場,我還以為windows的IE支捕獲呢!!不過也不是完全沒有收穫,如果你也像我這麼整了,你要注意IE6會折騰兩次的,不過只是在你點擊的時候才會這樣,如果用tab切換獲得焦點,就只會觸發A標籤的focus事件了。

還有在Firefox(我測試的最新的3.5)中,可千萬別window.onfocus,不然你就掛了!很抱歉用FIREFOX看我這篇文章的人,OK了幾次後,你就掛了!!呵呵!!!

What is ‘this'?

IE在前面給了我“驚喜”,this也給我驚喜。當然主要是我以前沒有注意到,但是YAHOO的工程師們早以發現了。this這個關鍵字是根據上下文來決定的,在我們的事件綁定的功能函數裡,this應該是指向當前的元素節點對象,應該是一個Element對象。我想這個大家應該好理解:

範例程式碼:


<span id="cnt0">
<a href=”#1″ id=”link0″>點擊我觸發捕獲型事件流</a>
</span>

<script type=”text/javascript”>
var link = document.getElementById('link1′);
link.onclick = function(){
alert(this.tagName);
};
</script>

這段代碼正式我在樣本(1)中的處理方式,樣本中this指向的a標籤這個element對象,所以我們可以得到a標籤的tagName這個屬性‘A'。樣本(2)裡,我使用一個相容的事件監聽函數:


function addListener(el, event, fn, bCapture){
var isCapture = bCapture ? bCapture : false;
try {
el.addEventListener(event, fn, isCapture);
}
catch (e) {
try {
el.attachEvent('on' + event, fn);
}
catch (e) {
el['on' + event] = fn;
}
}
}

在我們的支援DOM事件流的瀏覽器裡,我們也可以得到正確的提示this.tagName為‘A'。this出現問題就在IE中,當我們使用attachEvent給元素繫結事件,現在你點擊下面的連結:

樣本(3):What is ‘this'?

範例程式碼:
<span id=”cnt2″>
<a href=”#1″ id=”link2″>What is ‘this'?</a>
</span>

function whatIsThis(){
if (this === window) {
alert('This is a window object');
}
alert('So, This.tagName is:'+ ‘‘'+ this.tagName +''。');
}

<script type=”text/javascript”>
var cntThree = document.getElementById('cnt2′), lnkThree = document.getElementById('link2′);

addListener(lnkThree, ‘click', whatIsThis);
addListener(cntThree, ‘click', whatIsThis);
</script>

看清楚了嗎?如果你在IE6~8中測試,那麼你點的是window對象而不是一個a標籤。暈倒!!!-_-! 所以你要小心,問題多多啊,要解決這個this關鍵字的問題,我給你的建議就是你可以考慮直接用傳統的'onclick'或者修改下前面的綁定事件監聽的函數:

修改後的代碼:

function addEvent(el, event, fn, obj, overrideContext, bCapture){
var context = el, isCapture = bCapture ? bCapture : false, wrappedFn = null;

if (overrideContext) {
if (overrideContext === true) {
context = obj;
}
else {
context = overrideContext;
}
}
wrappedFn = function(){
return fn.call(context);
};
try {
el.addEventListener(event, wrappedFn, isCapture);
}
catch (e) {
try {
el.attachEvent('on' + event, wrappedFn);
}
catch (e) {
el['on' + event] = wrappedFn;
}
}
}

樣本(4):再點點我,看我的‘this'是什嗎?

好了就這麼多了,不知道對你有沒有協助,最後說明下,本文中的部分觀點參考至《Javascript進階程式設計》(很好的一本書,推薦大家看看!),addEvent函數借鑒了YUI2.7的_addListener方法,這裡也要謝謝YUI那些牛人,向他們致敬!

靜態頁訪問地址:http://img.jb51.net/online/jsevent/event.htm(如果你也想體驗下我的“驚喜”,請用IE6訪問,點擊第一個樣本連結,但千萬不要用Firefox,否則會掛掉,別說我沒有提醒你!!!)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.