1. 程式人生 > >事件繫結(事件處理程式)

事件繫結(事件處理程式)

事件處理程式(事件繫結):響應某個事件的函式就叫做事件處理程式(或事件偵聽器)。

為事件指定處理程式的方式:

1.HTML 事件處理程式:某個元素支援的每種事件,都可以使用一個與相應事件處理程式同名的 HTML 特性來指定。這個特性的值應該是能夠執行的 JavaScript 程式碼。

示例程式碼:

<input type="button" value="Click Me" onClick="alert('Click')" />

當單擊這個按鈕時,就會顯示一個顯示框。這個操作是通過指定 onClick特性並將一些 JavaScript 程式碼作為它的值來定義的。在 HTML 中定義的事件處理程式可以包含要執行的具體動作,也可以呼叫在頁面其他地方定義的指令碼:

<script type="text/javascript">
    function showMessage(){
        alert("Hello world");
    }
</script>
<input type="button" value="Click Me" onClick="showMessage()"/>

在這個例子中,單擊按鈕就會呼叫 showMessage()函式。這個函式是在一個獨立的<script> 元素中定義的,也可以被包含在一個外部檔案中。事件處理程式中的程式碼在執行時,有權訪問全域性作用域的任何程式碼。

這樣指定事件的特點:
1.這樣會建立一個封裝著元素屬性值的函式:這個函式中有一個區域性變數 event,也就是事件物件。

<!--輸出"click"-->
<input type="button" value="Click Me" onClick="alert(event.type">

通過 event變數,可以直接訪問事件物件,可以不用自己定義它,也不用從函式的引數列表中讀取。在這個函式內部, this值等於事件的目標元素,例如:

<!--輸出 “Click ME”-->
<input type="button" value="Click Me" onClick="alert(this.value)">

2.它拓展作用域的方式:這個函式內部,可以像訪問區域性變數一樣訪問 document及該元素本身的成員。這個函式使用 with像下面這樣拓展作用域:

function(){
    with(document){
        with(this){
            //元素屬性值
        }
    }
}

這樣,事件處理程式要訪問自己的屬性就簡單很多。下面這行程式碼與前面的例子效果相同:

<!--輸出 "Click Me"-->
<input type="button" value="Click Me" onClick="alert(value)">

如果當前元素是一個表單輸入元素,則作用域中還包含訪問表單元素(父元素)的入口,這個函式就變成如下所示:

function(){
    with(document){
        with(this.form){
            with(this){
                //元素屬性值
            }
        }
    }
}

實際上,這樣擴充套件作用域的方式,就是想讓事件處理程式無需引用表單元素就能訪問其他表單欄位。例如:

<form method="post">
    <input type="text" name="username" value="">
    <input type="button" value="Echo UserName" onClick="alert(username.value)">
<form>

在這個例子中,單擊按鈕會顯示文字框中的文字。值得注意的是,這裡直接引用了 username 元素。

在 HTML 中指定事件處理程式的缺點:

1.存在一個時差問題:因為使用者可能會在 HTML 元素一出現在頁面上就觸發相應的事件,但當時的事件處理程式有可能尚不具備執行條件。但是可以通過將 HTML 事件處理程式封裝在一個 try-catch 塊中,以便錯誤不會浮出水面:

<input type="button" value="Click Me" onclick="try(showMessage();)catch(ex){}">

2.這樣擴充套件事件處理程式的作用域鏈在不同瀏覽器中導致不同結果。不同 JavaScript 引擎遵循的識別符號解析規則略有差異,很可能會在訪問非限定物件成員時出錯。
3.HTML 與 JavaScript 程式碼緊密耦合。如果要更換事件處理程式,就要更改兩個地方:HTML 程式碼和 JavaScript 程式碼。

2.DOM0 級事件處理程式:通過 JavaScript 指定事件處理程式的傳統方式,就是將一個函式賦值給一個事件處理程式屬性。

使用 JavaScript 指定事件處理程式,首先必須取得一個要操作的物件的引用;每個元素(包括 window 和 document)都有自己的事件處理程式屬性,這些屬性通常全部小寫;將這種屬性的值設定為一個函式,就可以指定事件出來程式。如下所示:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert("Click");
};

我們通過文件物件取得了一個按鈕的引用,然後為它指定了 onclick事件處理程式,但是注意:在這些程式碼執行以前不會指定事件處理程式,因此如果這些程式碼在頁面中位於按鈕後面,就有可能在一段時間內怎麼單擊都沒有反應。
使用 DOM0 級方法指定的事件處理程式被認為是元素的方法。因此,這時候的事件處理程式是在元素的作用域中執行;也就是說,程式中的this引用當前的元素。如下例子:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert (this.id);  //"myBtn"
};

單擊按鈕顯示的是元素的 ID,這個 ID是通過 this.id取得的(實際上可以在事件處理程式中通過this訪問元素的任何屬性和方法)。以這種方式新增的時間處理程式會在事件流的冒泡階段被處理。也可以刪除通過 DOM0 級方法指定的事件處理程式。只要將事件處理屬性的值設定為 null即可:

btn.onclick = null; //刪除事件處理程式

將事件處理程式設定為null之後,再點選按鈕將不會有任何動作發生。

3.DOM2 級事件處理程式:“DOM2 級事件”定義了兩個方法,用於處理指定和刪除事件處理程式的操作:addEventListener()removeEventListener()。所有DOM節點中都包含這兩個方法,並且他們都接受3個引數:要處理的事件名、作為事件處理程式的函式和一個布林值。最後這個布林值引數若是 true ,表示在捕獲階段呼叫事件處理程式;如果是 fasle,表示在冒泡階段呼叫事件處理程式

要在按鈕上為click事件新增事件處理程式,可以使用下列程式碼:

var btn = document.getElementById("myBtn");
btn.addEventListenenr("click",function(){
    alert(this.id);
},false);

上面的程式碼為一個按鈕添加了onclick事件,而且該事件會在冒泡階段被觸發(因為最後一個引數是 fasle)。與DOM0 級方法一樣,這裡新增的事件處理程式也是在其依附的元素的作用域中執行。使用 DOM2 級方法新增事件處理程式的主要好處是可以新增多個事件處理程式。

var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
    alert(this.id);
},false);
btn.addElementListener("click"),function(){
    alert("Hello World!");
}.false);

這裡為按鈕添加了兩個事件處理程式。這兩個事件處理程式會按照新增他們的順序觸發,因此首先會顯示元素的ID,其次會顯示”Hello world”訊息。通過addEventListener()新增的事件處理程式只能使用removeEventListener()來移除;移除時傳入的引數與新增處理程式時使用的引數相同。這就意味著通過addEventListener()新增的匿名函式將無法移除,如下面的例子所示:

var btn = document.getElement("myBtn");
btn.addEventListener("click",function(){
    alert(this.id);
},false);
//這裡省略了其他程式碼
btn.removeEventListener("click",function(){//沒有用
    alert("this.id");
},false);

在這個例子中,我們使用addEventListener()添加了一個事件處理程式。雖然呼叫removeEventListener()時看似使用了相同的引數,但實際上,第二個引數與傳入addEventListener()中的那一個是完全不同的函式。而傳入removeEventListener()中的事件處理程式必須與傳入addEventListener()中的相同,如下面的例子所示:

var btn = document.getElementById("myBtn");
var handler = funciton(){
    alert(this.id);
};
btn.addEventListener("click",handler,false);
//這裡省略了其他程式碼
btn.removeEventListener("click",handler,false);  //有效

大多數情況下,都是將事件處理程式新增到事件流的冒泡階段,這樣可以最大限度的相容各種瀏覽器。最好只在需要在事件達到目標之前截獲它的時候將時間處理程式新增到捕獲階段。如果不是特別需要,不建議在事件捕獲階段註冊事件處理程式。

4.IE 事件處理程式:IE 實現了與 DOM 中類似的兩個方法: attachEvent()detachEvent(),這兩個方法接受相同的兩個引數:事件處理程式名稱與事件處理程式函式。由於 IE8 及更早版本只支援事件冒泡,所以通過 attchEvent()新增的事件處理程式都會被新增到冒泡階段。

程式碼示例:

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
    alert("Clicked");
});

注意,attachEvent()第一個引數是“onclick”,而非 DOM的 addEventListener()方法中的“click”。

在 IE 中使用 attachEvent()與使用 DOM0級方法的主要區別在於事件處理程式的作用域。在使用 DOM0 級方法的情況下,事件處理程式會在其所屬元素的作用域內執行;在使用 attachEvent()方法的情況下,事件處理程式會在全域性作用域中執行,因此 this 等於 window 。

程式碼示例:

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert(this === window);
//true
});

在編寫跨瀏覽器的程式碼時,牢記這一區別非常重要。與addEventListener()類似,attachEvent()方法也可以用來為一個元素新增多個事件程式。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert("Clicked");
});
document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert("Hello world");
});

這裡呼叫了兩次attachEvent(),為同一個按鈕添加了兩個不同的事件處理程式。不過,與 DOM 方法不同的是,這些事件處理程式不是以新增它們的順序執行,而是以相反的順序被觸發。單擊這個列子中的按鈕,首先看到的是“Hello world!”,然後才是“Clicked”。
使用 attachEvent()新增的事件可以通過detachEvent()來移除,條件是必須提供相同的引數。與 DOM 方法一樣,這也意味著新增的匿名函式將不能被移除。不過,只要能夠將對相同函式的引用傳給detachEvent(),就可以移除相應的事件處理程式。

程式碼示例:

var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
btn.attachEvent("onclick",handler);
//這裡省略了其它程式碼
btn.detachEvent("onclick",handler);

注意:支援 IE 事件處理程式的瀏覽器有 IE 和 Opera.

5.跨瀏覽器的事件處理程式

為了以跨瀏覽器的方式處理事件,不少開發人員會使用能夠隔離瀏覽器差異的 JavaScript 庫,還有一些開發人員會自己開發最合適的事件處理的方法。要保證處理事件的程式碼能在大多數瀏覽器下一致地執行,只需關注冒泡階段
第一個要建立的方法是addHandler(),它的職責是視情況分別使用 DOM0 級方法、DOM2級方法或 IE 方法來新增事件。這個方法屬於一個名叫 EventUtil 的物件。addHandler()方法接受3個引數:要操作的元素、事件名稱和事件處理程式函式。
addHandler()對應的方法是removeHandler(),它接受相同的引數。這個方法的職責是移除之前新增的事件處理程式——無論該事件處理程式是採用什麼方式新增到元素中的,如果其他方法無效,預設採用 DOM0 級方法。

EventUtil 的用法如下:

var EventUtil = {
    addHandler : function(element,type,handler){
        if(element.addEventListener){
            element.addEventListener(type,handler,false);
        }else if(element.attachEvent){
            element.attachEvent("on"+type,handler);
        }else{
            element["on"+type]= handler;
        }
    },
    removeHandler : function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);
        }else if(element.detachEvent){
            element.detachEvent("on"+type,handler);
        }else{
            element["on"+type] = null;
        }
    }
};

這兩個方法首先都會檢測傳入的元素中是否存在 DOM2 級方法。如果存在 DOM2 級方法,則使用該方法:傳入事件型別、事件處理程式函式和第三個引數 false(表示冒泡階段)。如果存在的是 IE 的方法,則採取第二種方案。注意,為了在 IE8 及更早版本中執行,此時的事件型別必須加上“on”字首。最後一種可能就是使用 DOM0 級方法(在現代瀏覽器中,應該不會執行這裡的程式碼)。此時,我們使用的是方括號語法來將屬性名指定為事件處理程式,或者將屬性設定為 null。

也可以像下面這樣使用 EventUtil 物件:

var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
EventUtil.addHandler(btn,"click",handler);
//這裡省略了其他程式碼
EventUtil.removeHandler(btn,"click",handler);

addHandler()removeHandler()沒有考慮到所有的瀏覽器問題,例如在 IE 中的作用域問題。不過,使用它們新增和移除事件處理程式還是足夠了。還需注意,DOM0 級對每個事件只支援一個事件處理程式。