我們都知道锻弓,在不同的瀏覽器下搓茬,添加和移除事件處理程序方式有所相同兽掰,要想寫出跨瀏覽器的事件處理程序竿滨,首先我們要了解不同的瀏覽器下處理事件處理程序的區(qū)別
在添加事件處理程序時addEventListener和attachEvent主要有以下幾個區(qū)別
參數(shù)個數(shù)不相同佳恬,這個最直觀捏境,addEventListener有三個參數(shù),attachEvent只有兩個毁葱,attachEvent添加的事件處理程序只能發(fā)生在冒泡階段垫言,addEventListener第三個參數(shù)可以決定添加的事件處理程序是在捕獲階段還是冒泡階段處理(我們一般為了瀏覽器兼容性都設(shè)置為冒泡階段)
第一個參數(shù)意義不同,addEventListener第一個參數(shù)是事件類型(比如click倾剿,load)筷频,而attachEvent第一個參數(shù)指明的是事件處理函數(shù)名稱(onclick,onload)
事件處理程序的作用域不相同前痘,addEventListener的作用域是元素本身凛捏,this是指的觸發(fā)元素,而attachEvent事件處理程序會在全局變量內(nèi)運行芹缔,this是window坯癣,所以剛才例子才會返回undefined,而不是元素id
為一個事件添加多個事件處理程序時最欠,執(zhí)行順序不同示罗,addEventListener添加會按照添加順序執(zhí)行,而attachEvent添加多個事件處理程序時順序無規(guī)律(添加的方法少的時候大多是按添加順序的反順序執(zhí)行的窒所,但是添加的多了就無規(guī)律了)鹉勒,所以添加多個的時候帆锋,不依賴執(zhí)行順序的還好吵取,若是依賴于函數(shù)執(zhí)行順序,最好自己處理锯厢,不要指望瀏覽器去解決皮官。
了解了這四點區(qū)別后我們可以嘗試寫一個瀏覽器兼容性比較好的添加事件處理程序方法
function addEvent(node, type, handler) {
if (!node) return false;
if (node.addEventListener) {
node.addEventListener(type, handler, false);
return true;
}
else if (node.attachEvent) {
node.attachEvent('on' + type, handler, );
return true;
}
return false;
}
這樣,我們解決了兩個問題
- 參數(shù)個數(shù)不同实辑,現(xiàn)在是三個參數(shù)捺氢,
- 現(xiàn)在都在事件冒泡階段觸發(fā)
- 第二個問題也得以解決,如果是IE剪撬,我們給type添加上on
但是如果handler內(nèi)有關(guān)于this操作的話摄乒,this指向依然是不同的,執(zhí)行順序的問題也還沒有解決方案残黑,這里需要我們自己注意馍佑,一般情況下,同一個對象不會添加很多事件處理程序梨水。
*接下來拭荤,我們來嘗試解決this指向問題:
function addEvent(node, type, handler) {
if (!node) return false;
if (node.addEventListener) {
node.addEventListener(type, handler, false);
return true;
}
else if (node.attachEvent) {
node.attachEvent('on' + type, function() { handler.apply(node); });
return true;
}
return false;
}
通過apply(),this指向了事件目標(biāo)疫诽,但是新的問題又來了舅世,我們這樣等于添加了一個匿名的事件處理程序旦委,無法用detachEvent取消事件處理程序,有很多解決方案雏亚,我們可以借鑒大師的處理方式缨硝,jQuery創(chuàng)始人John Resig是這樣做的
function addEvent(node, type, handler) {
if (!node) return false;
if (node.addEventListener) {
node.addEventListener(type, handler, false);
return true;
}
else if (node.attachEvent) {
node['e' + type + handler] = handler; //handler中的this指向node 即事件目標(biāo)
node[type + handler] = function() {
node['e' + type + handler](window.event);
};
node.attachEvent('on' + type, node[type + handler]); //調(diào)用handler函數(shù),并且傳入事件對象作為參數(shù)
return true;
}
return false;
}
在取消事件處理程序的時候
function removeEvent(node, type, handler) {
if (!node) return false;
if (node.removeEventListener) {
node.removeEventListener(type, handler, false);
return true;
}
else if (node.detachEvent) {
node.detachEvent('on' + type, node[type + handler]);
node[type + handler] = null;
}
return false;
}
這里很巧妙地利用了閉包罢低,看起來很不錯追葡。
事件對象的不同屬性方法
*DOM中的事件對象和IE中的事件對象有不同的屬性/方法
首先是DOM中事件對象有不同的屬性/方法:
屬性/方法 | 類型 | 讀/寫 | 方法 |
---|---|---|---|
bubbles | Boolean | 只讀 | 事件是否冒泡 |
cancelable | Boolean | 只讀 | 事件是否冒泡 |
currentTarget | Element | 只讀 | 事件處理程序當(dāng)前處理元素 |
detail | Integer | 只讀 | 與事件相關(guān)細(xì)節(jié)信息 |
eventPhase | Integer | 只讀 | 事件處理程序階段:1 捕獲階段,2 處于目標(biāo)階段奕短,3 冒泡階段 |
preventDefault() | Function | 只讀 | 取消事件默認(rèn)行為 |
stopPropagation() | Function | 只讀 | 取消事件進(jìn)一步捕獲或冒泡 |
target | Element | 只讀 | 事件的目標(biāo)元素 |
type | String | 只讀 | 被觸發(fā)的事件類型 |
view | AbstractView | 只讀 | 與事件關(guān)聯(lián)的抽象視圖宜肉,等同于發(fā)生事件的window對象 |
然后是IE中的:
屬性/方法 | 類型 | 讀/寫 | 方法 |
---|---|---|---|
cancelBubble | Boolean | 讀/寫 | 默認(rèn)為false,設(shè)置為true后可以取消事件冒泡 |
returnValue | Boolean | 讀/寫 | 默認(rèn)為true翎碑,設(shè)為false可以取消事件默認(rèn)行為 |
srcElement | Element | 只讀 | 事件的目標(biāo)元素 |
type | String | 只讀 | 被觸發(fā)的事件類型 |
雖然DOM和IE的event對象不同谬返,但基于它們的相似性,我們還是可以寫出跨瀏覽器的事件對象方案日杈,可以不用擔(dān)心瀏覽器的問題會影響我們功能的實現(xiàn)
function getEvent(e) {
return e || window.event;
}
function getTarget(e) {
return e.target || e.scrElement;
}
function preventDefault(e) {
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
}
function stopPropagation(e) {
if (e.stopPropagation)
e.stopPropagation();
else
e.cancelBubble = true;
}
*備注:關(guān)于事件相關(guān)的兼容
onclick-----------------------------------------都支持
attachEvent()-----------------------------------IE8
addEventListener()------------------------------IE9以前不支持
stopPropagation();preventDefault()--------------IE9以前不支持
returnValue-------------------------------------不支持preventDefault()方法取消默認(rèn)行為時的解決方法
cancelBubble=true-------------------------------不支持stopPropagation()方法阻止冒泡時的解決方法