JavaScript — event介紹以及兼容處理

JavaScript — event介紹以及兼容處理

1.事件流

瀏覽器發展到第四代時(IE4及 Netscape Communicator 4),瀏覽器開發團隊遇到一個問題:頁面的哪個部分會擁有某個特定的事件?可以想象在一張紙上的一組同心圓,如果把手指放在圓心上,那麼你的手指指向的不是一個圓,而是紙上的所有圓。即在點擊一個按鈕時,不僅點擊了按鈕,也點擊了整個頁面。

事件流描述的是從頁面中接收事件的順序。不過IE 和 Netscape 開發團隊提出的想法差不多完全相反。

IE的事件流是事件冒泡流,而 Netscape Communicator 的事件流是事件捕獲流。

1.1 事件冒泡

IE的事件流叫做事件冒泡(event bubbling),即事件開始時是由最具體的元素接收,然後逐級向上傳播到較爲不具體的節點。

備註:所有現代的瀏覽器都支持事件冒泡,但是在具體實現上還有一些差別。IE5.5 以及更早的版本中的事件冒泡會跳過 html 元素,即從body直接到 document。而IE9、Firefox、Chrome和Safari則將事件一直冒泡到window對象。

1.2 事件捕獲

Netscape Communicator 團隊提出的另一種事件流叫做事件捕獲。思想是不太具體的節點應該更早的接收到事件,而最具體的節點應該最後接收到事件。

事件捕獲的用意在於事件到達預定目標之前捕獲它。

雖然 事件捕獲 是Netscape Communicator 唯一支持的事件流模型,但是IE9、Firefox、Opera、Chrome和Safari目前也都支持這種事件流模型。

儘管“DOM2級事件”規範要求事件應該從document對象開始傳播,但是這些瀏覽器都是從window對象開始捕獲事件的。

由於老版本的瀏覽器不支持,因此很少有人使用事件捕獲。

1.3 DOM 事件流

“DOM2級事件”規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。首先發生的是事件捕獲,這爲截獲事件提供了機會。然後是實際的目標接收到事件。最後一個階段是冒泡階段,可以在這個階段對事件作出響應。

2. 事件處理程序

2.1 HTML 事件處理程序

如下:

<input type="button" value="click" onclick="alert('hello')" />

2.2 DOM0級事件處理程序

通過JavaScript指定事件處理程序的傳統方式,就是將一個函數賦值給一個事件處理程序屬性。這種爲事件處理程序賦值的方法是在第四代Web瀏覽器中出現的,而且至今仍然爲所有現代瀏覽器支持。一是簡單,二是具有跨瀏覽器優勢。

每個元素都有自己的事件處理程序屬性,這些屬性通常全部小寫,例如:onclick。將這種屬性的值設置爲一個函數,就可以制定事件處理程序。

如下例:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id); // "myBtn"
}
btn.onclick = null; // 刪除事件處理程序

以這種方式添加的事件處理程序會在事件流的冒泡階段被處理。

2.3 DOM2級事件處理程序

“DOM2級事件”定義了兩個方法,用於處理指定和刪除事件處理程序的操作:addEventListener()和removeEventListener()。都接受三個參數:要處理的事件名、作爲事件處理程序的函數和一個布爾值。最後這個布爾值參數如果是true,表示在捕獲階段調用事件處理程序;如果是false,表示在冒泡階段調用事件處理程序。默認是 false。

使用DOM2級方法添加事件處理程序的主要好處是可以添加多個事件處理程序。

var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){alert(this.id)}) // this 指的是元素本身
btn.addEventListener("click",function(){alert("hello word!"))})

通過 addEventListener 添加的事件,只能通過 removeEventListener 移除掉,移除時的參數需要和傳入的參數相同。這也意味着傳入的匿名參數無法移除。因此儘量避免使用匿名函數。

大多數情況下,都是把事件處理程序添加到事件流的冒泡階段,這樣可以最大限度的兼容各種瀏覽器。萬不得已的時候,再添加到捕獲階段。

2.4 IE事件處理程序

IE實現了與DOM中類似的兩個方法,attachEvent() 和 detachEvent()。這兩個方法接收兩個參數,因爲只支持冒泡。
與addEventListener 和 removeEventListener 不同的是,接受的第一個參數,必須帶on。如單擊事件,爲"onclick";還有添加多個事件的時候,此方法按照添加的順序反向執行。

此方法與DOM0級方法的主要區別是在於事件處理程序的作用域。此方法的事件處理程序會在全局作用域中運行,其中的this爲window

2.5 跨瀏覽器的事件處理程序

因爲不同瀏覽器對於事件的處理不一樣,所以可以手寫一些事件兼容方法。如下:

var EventUtil = {
    // 添加事件處理程序
    addHandler: function(element, type, handler) {
        if (element.addEventListener) { // DOM2級 事件處理程序,this 指向元素本身。按照添加的順序正向執行
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) { // IE 事件處理程序,this 指向 window。按照添加的順序反向執行
            element.attachEvent("on" + type, handler);
        } else { // DOM0級 事件處理程序。只能綁定一個事件處理程序
            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;
        }
    },
    // 獲取 event 對象。window.event 爲 IE 瀏覽器的獲取方式
    getEvent: function(event) {
        return event ? event : window.event;
    },
    // 獲取event的target。 event.srcElement 只對老版本的 IE 瀏覽器有效
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    // 取消事件的默認行爲
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false; // IE
        }
    },
    // 阻止事件冒泡
    stopPropagation: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true; // IE
        }
    }
}

3.事件對象小記

3.1 非IE瀏覽器

  1. target 和 currentTarget 意義不同。 currentTarget 指的是處理這個事件的節點;target 指的是 觸發事件的實際目標。
  2. preventDefault 可以阻止特定事件的默認行爲。如a標籤的跳轉,form表單的提交,複選框的選中等等。但是隻有 cancelable 爲true的纔可以使用。
  3. stopPropagation 可以阻止事件冒泡。比如 button 和 body都有對click事件的處理程序,如果不作特殊處理,這兩個事件處理程序都會執行,而是用stopPropagation則會阻止body中事件的觸發;

3.2 IE中事件對象略有不同:

  1. cancelBubble,默認值爲false,將其設置爲true,可阻止事件冒泡;類比stopPropagation
  2. returnValue,默認值爲true,將其設置爲false,可以阻止事件的默認行爲;類比preventDefault
  3. IE的DOM0級事件,添加事件處理程序的時候,event是作爲window的一個屬性而存在,即需要通過window.event來獲取;
  4. 如果是使用 attachEvent() 的方法添加事件處理程序,會有一個event對象作爲參數傳遞到方法中,同時也可以通過 window.event來獲取;
  5. srcElement與DOM中的target屬性相同。

備註:事件處理程序的作用域是根據指定他的方式確定的,所以不能認爲this始終等於時間目標。還是使用 srcElement 保險。

如下例:

<button id="myBtn">event</button>
<script>
    var btn = document.getElementById('myBtn');
    btn.onclick = function (){
        alert(window.event.srcElement === this); // true
    }
    btn.attachEvent('onclick',function(event){
        alert(event.srcElement === this) // false
        alert(this === window) // true
    })
</script>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章