在Javascript裡面,IE跟Fx用的event handler完全不同,IE用的是event bubbling,而Fx用的是event capture。

event bubbling會利用DOM Model的樹狀結構,一步一步的往root(window)做事件的發生,比如有一個button在form裡面,按下button的事件發生順序就是,<button> -> <form> -> <body> -> <html> -> document -> window。

而event capture則是完全相反,會先由window開始,依序為window -> document -> <html> -> <body> -> <form> -> <button>,兩者並不相容。所以我們在撰寫跨瀏覽器的javascript時常常會遇到這種問題,像我的UMiP就是這個樣子 = =

這篇文章的原作者做了很多event handler的建議,真的很重要,我剛剛就是看這篇文章才了解javascript的各種event handler,也才能夠完成我的UMiP support IE,麻煩呀!




最近為了撰寫公司內部的地圖應用程式,突然之間由 Java Programmer 的角色,變成 Javascript Programmer。為了成為頂尖的 AJAX Programmer,又是寫地圖應用程式。當然是以 Google Map 為參考的藍本,完全仿造 Google Map所提供的功能,打造公司內部的 AJAX 地圖應用程式。等於是對 Google Map 做反向工程,深入 AJAX 底層技術。

切入正題,在大量閱讀 Javascript 相關文件,由網路中找出一些大師級的、對Javascript有深入剖析的文章,就在這裡以半中譯、半介紹、加上一些討論的方式,介紹給大家。有一些多餘的英文說明,就不中譯。

本篇文章的主體來自於Quirksmode的Introduction to Events

以下斜體藍色字為原文中譯,正體字為筆者的說明與討論。

Event(事件)是任何Javascript程式的心跳。

原文是 Beating of heart,直譯就是心跳。中文意思就是說,Javascript程式的驅動核心就是事件。我也是這樣覺得,寫了一個多月的 Javascript,由不太懂 Javascript 直接切入 Javascript 的核心,覺得以前寫的 Javascript: DOM、DHTML、浮動選單、字體變化、動態產生物件、AJAX…等等,都太過小兒科了。網路上早就有一堆 Top Programmer把Javascript研究的透澈深入。鑽得越深,越覺得自己是井底之蛙!坊間的一些 DHTML、AJAX的中文書乃至原文中譯的書,也都太淺。

接下來網將先簡單介紹什麼是事件處理器(Event Handler),如何撰寫跨瀏覽器的script。並且用幾個頁面詳細介紹事件處理器的細節。

沒有事件,就沒有script。幾乎所有javascript網頁,都是由事件驅動,並且與使用者互動。

因此javascript需要知道使用者的行為,並且適當的回應。接下來的幾頁說明撰寫事件script的最佳方式。

Event Handler會和某個Element產生關連(技術用語叫做attach),當Element觸動了該事件,該事件就會去執行相對應的javascript程式碼。大多數是一個function。

Event Handler最開始是由Netscape 2開始支援Javascript時開始有的,當時只支援幾個event。最有名的就是mouseover和mouseout,當滑鼠移到某個物件上時,產生字體、顏色、圖片的變化,移走時就又恢復原狀。在當時都還是靜態網頁時,真的讓人印象深刻!還有onsubmit,讓client端(browser)可以進行資料確認,就不需要等submit到web server端檢查。那時候的broswer也可知道表格(form)欄位的值,以及物件是否focus,也可以知道page是否載入完成或者開始unloading。這幾個功能現在看來都相當基本,但在那時候,卻有造成革命性的影響。互動式網頁變得可能。

unload這個event不知道在何時使用呢?等我知道最適當的使用時機,再告訴各位。

最古老的表格事件處理器就像下面那樣的程式碼

<a href=”link.html” onclick=”alert(’Hi! Hello World!’)”/>

所有的browser都支援這樣的方式。
後來新的browser增加了許多新的事件,事件處理器的attach方式也有所改變。web developer可以使用javascript來註冊(register)事件處理器。這樣的好處式可以把事件處理與html碼分開來,不會混合在一起。

到了Nerscape 4的時候,borswer已經可以偵測某事件發生,以及滑鼠的位置,按了什麼鍵。這個時候,browser廠商開始思考,當子物件與父物件同時擁有相同的事件處理器時,譬如:

<a href=”link.html” onclick=”alert(’Hi! Hello World!’)”>

<img scr=”img.jpg” onclick=”alert(’Hi! Hello Image!’)”/>
/a>


哪個事件要先處理?
在這場 browser 大戰,Microsoft與Netscape採用了完全不相容的做法。後來,W3C推出DOM Event specification。雖然有嚴重的漏洞,不過W3C基本上依據的是Netscape的事件處理模式。並且提供了許多新的有趣的功能,也解決一些舊式的Netscape事件模式的問題。

就是因為有這三種不同的事件模式,使得現在的各種不同的browser對事件的回應方式有所不同。

三種不同的事件處理模式
1. Netscape 處理模式
2. Microsoft 處理模式
3. W3C 的 DOM 事件規範 (Event Specification)

Browser 的相容性問題
千萬不要使用瀏覽器偵測技術!

市場上有許多大大小小的 Browser 程式,每一種遵循的標準不盡相同。所以千萬不要使用瀏覽器的偵測技巧,因為瀏覽器所依循的標準可能是混合的。也就是說在某個地方遵循某個標準,在另一個地方,遵循的又是不同的標準。各版本之間也有相容性的問題。

DHTML的物件偵測與Event的偵測不要混爲一談。舉例來說,Opera 6支援 W3C的DOM標準,但不支援W3C的事件模式。這也是為何不要使用瀏覽器偵測的主要原因。

正確的回答問題

處理事件時要記得幾個重要的問題,首先,不要用三種不同的事件處理模式來判斷。其中包括了:四種不同的事件註冊模式、兩種不同的事件存取模式,以及兩主種不同的事件發生順序。

了解這幾個問題,就可以正確處理事件的回應處理程式。

Peter-Paul Koch整理了幾個核心問題,讓我們去思考與解決:

Q. 事件會在哪裡發生呢?
A. 很多地方都可能。但不全部的瀏覽器支援所有的事件。

Q.如何將事件處理器註冊(register)到HTML的元件上?
A.有四種註冊模式: inline, 傳統模式, W3C, 以及 Mircrosoft。第一種inline所有的瀏覽器都支援。

Q.如何避免事件的預設行為?
A.應該先知道什麼是事件的預設行為呢? 譬如在一個form裡的submit按鈕的預設行為就是submit form。一個 a href='’… HTML元素的預設事件就是開啟新的連結。避免預設行為的方式就是在相對應的 script 傳回 (return) false。

Q. 如何由事件中取得更多的資訊?
A. 有兩種模式: W3C/Netscape和Microsoft。解決這個問題最簡單的方式是:

function W3C_evnetHandler(e) {
// e 可以取得事件
}
function Microsoft_eventHandler() {
// window.event 可以取得事件
}

Q. 如何取得事件內的訊息 properties?
A. 有相容性的問題。請參考事件屬性表格

Q. 如果物件容器內還有另一個物件。也就是子物件與父物件有相同的事件處理器。同時被驅動時何者先處理呢?
A. 兩種模式: Event capture和event bubbling。
以上是撰寫跨瀏覽器的事件處理程式必須注意的,每個事件的處理模式不盡相同,必須分開來處理與測試。過程如下:

先確定一種事件註冊的模式,然後確認在所有的瀏覽器上都可以正確偵測。
接下來讀取事件的屬性 (properties),確認都讀得到。
最後再解決事件發生先後順序的問題(很少需要)。

分開來解決每種事件,這樣子就可以確保跨瀏覽器的相容性問題。
撰寫事件處理程式(script)

1. 首先註冊事件處理器,註冊方式四種: inline、傳統、W3C 和 Microsoft。建議使用傳統的方式,沒有瀏覽器相同性的問題。所有的瀏覽器都支援。

element.onclick = doSomething;
if (element.captureEvents) element.captureEvents(Event.CLICK); // 這一段碼我沒用過,要測試一下目的。

function doSomething(e) {
if (!e) {
var e = window.event; // ?? e 這時候有 scope 的問題嗎?
}
}

如果想要存取觸發物件的元件本身,有兩個方式: 一種是使用 this, 或者使用 event 的 target/srcElement屬性。

使用 target/srcElement時要留意,如果是由 event capture或者event bubbling來的事件。target/srcElement指向最開始觸發事件的物件本身。

2. 讀取事件屬性

事件屬性是相容性最多問題的地方,詳細參考事件屬性表。然後一一檢查與確認


function doSomething(e) {
if (!e) var e = window.event;
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
}

3. 最後處理事件順序的問題

決定使用 bubble up 或 event capture模式。如果兩種都不要發生。可以停止事件的擴散。

function doSomething(e) {
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}

以上文章的主體來自於Quirksmode的Introduction to Events,有興趣的朋友可自行參考。

參考資料:
Javascript程式設計的核心 - Event

全站熱搜

kewang 發表在 痞客邦 留言(1) 人氣()