文章目錄
在這一章我會講解給元素註冊事件的最好的一種辦法,那就是:確保一個特定的事件在特定的HTML元素上發生並且能運行特定的指令碼。
在最古老的JavaScript瀏覽器裡註冊事件只能通過內聯模式。自從DHTML從根本上改變了你操作頁面的方法,事件的註冊就必須有擴充性而且要有很強的適應性。所以就必須有相應的事件模型。Netscape在第三代瀏覽器中就開始了,IE在第四代瀏覽器開始。
因為Netscape 3就開始支援這種新的事件註冊模型,在瀏覽器戰爭前就是事實上的標準。所以微軟不得不也是最後一次為了網上那些數不清的使用了Netscape事件處理模型的頁面在相容性上做出了讓步。
所以這兩個瀏覽器,事實上也是所有的瀏覽器都支援下面的代碼:
element.onclick = doSomething;
這是註冊一個事件的最好的辦法。無論什麼時候使用者點擊了這個HTML元素,那麼doSomething()都會執行。這是唯一一個能夠跨瀏覽的註冊事件的最好的辦法,深刻的理解這個模型和他的限制也是非常重要的。
因為沒有官方的標準,所以我暫且稱為傳統事件註冊模型(traditional event registration model)。同時,w3c也標準化了事件註冊,微軟也推出了進階模式,但是傳統模式依然能很好的運行。
進階事件註冊程式
從Netscape 3/IE 4開始,JavaScript能夠識別元素上的一系列事件的屬性。大多數HTML元素都有onclick,onmouseover,onkeypress等等屬性。那些元素有哪些屬性--哪些元素支援哪些事件--都依賴於瀏覽器。
這些屬性對於他們本身也不是什麼新穎的東西。在最古老的JavaScript瀏覽器裡面就已經存在了。
<a href="somewhere.html" onclick="doSomething()">
這裡的A標籤就有一個onclick參數,在JavaScript裡面就成為了A元素的屬性。那些古老的瀏覽器的事件處理常式只能通過在頁面原始碼裡面設定元素的參數這個辦法來註冊。如果你想讓這個指令碼在所有的A標籤執行,那麼你就需要再所有的連結上面加上onclick事件。
有了傳統事件註冊模型的到來,這些onclick,onmouseover或者HTML元素的其他事件處理就都可以通過JavaScript來註冊了。現在你可以添加、修改或者刪除一些事件處理常式而不用動HTML的一絲一毫。當你通過DOM來訪問HTML元素的時候你就可以像下面這樣寫代碼了:
element.onclick = doSomething;
現在我們的樣本函數doSomething()就註冊在了element元素的onclick屬性上,而且當使用者點擊了這個元素函數就會執行。注意事件的名字必須都是小寫。
刪除這個事件處理常式,只要簡單的讓點擊事件為空白就行了:
element.onclick = null;
事件處理常式跟普通的JavaScript函數一樣。即使事件沒有發生的時候他也能執行。如果你則這樣寫:
element.onclick()
那麼doSomething一樣會執行。雖然如果是一個不知道做什麼或者產生錯誤的函數,這也沒有真實的事件發生。所以這是一種很少用來執行事件處理常式的方法。
微軟的IE5.5和更高版本的IE還有一個fireEvent()方法來完成同樣的事情。使用如下:
element.fireEvent('onclick')
沒有括弧
需要注意的是註冊一個事件處理常式的時候你不能使用括弧。onclick方法會被設定成為另外一個函數。如果你這樣寫
element.onclick = doSomething();
那麼這個函數就會執行並且它的結果會被註冊到onclick上。這可不是我們所期望的,我們只是希望在事件發生的時候函數能夠執行。另外函數寫出來是為了在事件發生的時候執行,如果沒有關聯的執行會造成嚴重的混亂和錯誤。
所以我們在事件處理常式中複製整個doSomething()方法。我們只是想在事件執行的時候執行這個函數。
this
在JavaScript裡this關鍵字通常指函數的所有者。如果this指向事件發生的HTML元素,那麼一切都是那麼的美好,你可以很簡單的做很多事情。
不幸的是,雖然this非常的強大,但是如果你不是明確的知道他怎麼運作的話使用起來還是比較難的。關於這個我在另一個地方有詳細的討論,在這我在傳統模式下做一些概述。
在傳統模式裡this工作如下;注意這個跟內聯模式稍微有些不同。現在this關鍵字在函數裡,而不是在HTML的參數上。這個區別後面會另外講的。
element.onclick = doSomething;
another_element.onclick = doSomething;
function doSomething() {
this.style.backgroundColor = '#cc0000';
}
如果你註冊了doSomething()作為任何一個HTML元素的click事件,那麼當使用者點擊那個元素的時候元素就得到一個背景。
匿名函數(Anonymous functions)
假設你想所有div在滑鼠經過的時候改變背景色,然後在滑鼠離開的時候返回背景色。正確的使用this,你可以這樣寫:
var x = document.getElementsByTagName('DIV');
for (var i=0;i<x.length;i++) {
x[i].onmouseover = over;
x[i].onmouseout = out;
}
function over() {
this.style.backgroundColor='#cc0000'
}
function out() {
this.style.backgroundColor='#ffffff'
}
這些代碼可以運行,沒問題。但是既然over()和out()都比較簡單,那麼就可以用一種更優雅的匿名函數的方法來寫:
...
for (var i=0;i<x.length;i++) {
x[i].onmouseover = function () {this.style.backgroundColor='#cc0000'}
x[i].onmouseout = function () {this.style.backgroundColor='#ffffff'}
}
反正onmouseover和onmouseout都是得到一個函數。與其拷貝over()和out(),不如直接定義一個事件處理常式在這個事件註冊的指令碼上。既然這些函數沒有名字,那麼他們就是匿名函數。
這兩種註冊事件處理常式的方法基本上一樣,唯一的區別就是第二種的代碼量少一些。我非常喜歡匿名函數並且我會在註冊一個簡單的事件處理常式的時候使用它。
問題
有一個小小的問題就是傳統模式下onclick屬性只能包含一個函數。當你想對一個事件註冊多個事件處理常式的時候就有問題了。
比如,你已經寫了一個可以拖動的模組。這個模組註冊在onclick事件處理常式上所以當你點擊它的時候就能開始拖動。你還寫了一個模組可以跟蹤使用者的點擊然後在onunload的時候發送資訊給伺服器,這樣就能知道你的頁面如何被使用的。這個模組也需要在元素上註冊一個onclick事件。
所以事情可能會是這樣:
element.onclick = startDragDrop;
element.onclick = spyOnUser;
這是就會發生錯誤。第二個註冊程式會覆蓋第一個,那麼當使用者點擊元素的時候就只有spyOnUser()執行。
解決辦法就是註冊一個包含兩個方法的方法:
element.onclick = function () {startDragDrop(); spyOnUser()}
靈活的註冊
但是假設你沒有在你網站的每個頁面都使用兩個模組。如果你還這樣寫:
element.onclick = function () {startDragDrop(); spyOnUser()}
你會得到一個錯誤資訊因為其中有個函數是未定義的。所以在註冊事件的時候要特別的小心。當我們在startDragDrop()可能已經註冊的時候還想註冊spyOnUser(),那麼我們可以這樣寫:
var old = (element.onclick) ? element.onclick : function () {};
element.onclick = function () {old(); spyOnUser()};
首先你定義一個變數old。如果元素已經有了一個onclick的事件處理常式,那麼就把它存入old,如果沒有,就設定old為一個空的function。現在你要給一個div註冊一個新的事件處理常式。那麼程式就會首先執行old(),然後執行spyOnUser()。現在新的事件處理常式添加在了元素上,之前的註冊過的(如果有)也被包含了。
最後一個問題:如果你想移除其中一個事件處理常式呢?現在我還不是很確定。你需要通過一些方法編輯element.onclick(),我還沒有研究過這個問題。
其他模式
我們看到傳統模式非常的簡單易用,但是當你給一個事件添加幾個程式的時候的解決辦法還是比較醜陋的。W3C的事件處理常式很好的解決了這個問題。
繼續
如果你想繼續學習,請看下一章。
原文地址:http://www.quirksmode.org/js/events_tradmod.html
第一次翻譯 大家多多包含