0 Event Object導論
支援DOM0、DOM2的瀏覽器和IE瀏覽器實現事件處理程式的不同,除了體現在新增事件處理程式的不同上,還體現在event物件的實現差異上,包括具體的屬性和方法。
在觸發DOM上的某個事件時,都會產生一個event物件,這個物件包含著所有與事件有關的資訊。一旦事件處理程式執行完成,event物件就會被銷燬。所有瀏覽器對event的支援方式包括:DOM事件物件和IE事件物件。
1.1 DOM0和DOM2支援的Event物件(針對IE之外的瀏覽器)
相容 DOM 的瀏覽器會將一個 event 物件傳入到事件處理程式中。無論指定事件處理程式時使用什麼方法(DOM0 級或 DOM2 級) ,都會傳入 event 物件。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
alert(event.type); //"click"
}, false);
在通過 HTML 特性指定事件處理程式時,變數 event 中儲存著 event 物件:
<input type="button" value="Click Me" onclick="alert(event.type)"/>
1.2 event物件常用屬性和方法
(1) type, String; 只讀; 被觸發的事件的型別,如click。
(2) currentTarget, Element,只讀,其事件處理程式當前正在處理事件的那個元素;
(3) target, Element, 只讀, 事件的目標;
在事件處理程式內部,物件 this 始終等於 currentTarget 的值(註冊事件處理程式的元素),而 target 則只包含事件的實際目標(事件直接觸發的元素)。如果直接將事件處理程式指定給了目標元素,則 this、 currentTarget 和 target 包含相同的值。但是考慮到有時候會使用事件冒泡機制,事件直接觸發的元素不一定是事件繫結的元素,例如:
//按鈕在body中,事件會冒泡到body
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("myBtn")); //true
};
當單擊這個例子中的按鈕時, this 和 currentTarget 都等於 document.body,因為事件處理程式是註冊到這個元素上的。然而, target 元素卻等於按鈕元素,因為它是 click 事件真正的目標。由於按鈕上並沒有註冊事件處理程式,結果 click 事件就冒泡到了 document.body,在那裡事件才得到了處理。
(4) preventDefault()方法:阻止特定事件的預設行為。例如,連結的預設行為就是在被單擊時會導航到其 href 特性指定的 URL。
var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
};
(5) stopPropagation()方法:用於立即停止事件在 DOM 層次中的傳播,即取消進一步的事件捕獲或冒泡。這個屬性在jQuery中很重要。
(6) eventPhase 屬性:可以用來確定事件當前正位於事件流的哪個階段。如果是在捕獲階段呼叫的事件處理程式,那麼 eventPhase 等於 1;如果事件處理程式處於目標物件(直接點選物件,一般是最內層物件)上,則 eventPhase 等於 2;如果是在冒泡階段呼叫的事件處理程式, eventPhase 等於 3。這裡要注意的是,儘管“處於目標”發生在冒泡階段,但 eventPhase 仍然一直等於 2。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.eventPhase); //2 ,最內元素在事件傳播結束後觸發
};
document.body.addEventListener("click", function(event){
alert(event.eventPhase); //1,DOM2事件觸發使用true引數是在傳播階段觸發
}, true);
document.body.onclick = function(event){
alert(event.eventPhase); //3,DOM0事件在冒泡階段觸發
};
當單擊這個例子中的按鈕時,首先執行的事件處理程式是在捕獲階段觸發的新增到 document.body中的那一個,結果會彈出一個警告框顯示錶示 eventPhase 的 1。接著,會觸發在按鈕上註冊的事件處理程式,此時的 eventPhase 值為 2。最後一個被觸發的事件處理程式,是在冒泡階段執行的新增到document.body 上的那一個,顯示 eventPhase 的值為 3。而當 eventPhase 等於 2 時,this、target和 currentTarget 始終都是相等的。
2.1 IE中的事件物件
IE中event物件的訪問方式取決於繫結事件處理程式的方法:
1)DOM0級方法: event物件作為window物件的一個屬性存在。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
var event = window.event;
alert(event.type); //"click"
};
2)如果事件處理程式是通過attachEvent()新增的,就有一個event物件作為事件處理程式的引數。
var btn = document.getElementById("myBtn");
alert(typeof attachEvent);
btn. attachEvent("click", function(event){
alert(event.type); //"click"
});
2.2 IE中event物件的屬性和方法
1)cancelBubble:預設值為false,但將其設定為true就可以取消事件冒泡(與DOM中
的stopPropagation()方法的作用相同);
2)returnValue:預設值為true,但將其設定為false就可以取消事件的預設行為(與
DOM中的preventDefault()方法的作用相同);
3)srcElement:事件的直接目標(與DOM中的target屬性相同);
4)type:被觸發的事件的型別。
srcElement:因為事件處理程式的作用域是根據指定它的方式來確定的, this 不一定總是等於事件目標。例如:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(window.event.srcElement === this); //DOM0級方法,true
};
btn.attachEvent("onclick", function(event){
alert(event.srcElement === this); //false
});
returnValue 屬性:只要將 returnValue 設定為 false,就可以阻止預設行為。
var link = document.getElementById("myLink");
link.onclick = function(){
window.event.returnValue = false;
};
cancelBubble 屬性:與 DOM 中的 stopPropagation()方法作用相同,都是用來停止事件冒泡的。由於 IE 不支援事件捕獲,因而只能取消事件冒泡;但 stopPropagatioin()可以同時取消事件捕獲和冒泡。例如:
//在單擊按鈕之後,只會顯示一個警告框。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("Clicked");
window.event.cancelBubble = true;
};
document.body.onclick = function(){
alert("Body clicked");
};
2.3 IE11的變化
但是在IE11釋出之後attachEvent函式被替換成了addEventListener。
var btn = document.getElementById("myBtn");
alert(typeof addEventListener);//function
btn.addEventListener("click", function(event){
alert(event.type); //"click"
});
IE11中srcElement屬性測試:
HTML:
<div id="wrapper">
<span id = "myBtn">the onlt one div</span>
</div>
JavaScript:
var wrapper = document.getElementById("wrapper");
alert(typeof addEventListener);
wrapper.addEventListener("click", function(event){
alert(event.target); //span
alert(event.currentTarget); //div
alert(event.srcElement); //span
alert(this);//div
});
結論:srcElement和target是相同的,都指向事件的直接元素,但是this和currentTarget都指向事件繫結元素。
如果需要阻止預設行為,直接使用在addEventListener()中使用event.preventDefault()即可。根據以上,可以得出初步的結論:在IE11瀏覽器中使用addEventListener()後,DOM2級event具有的屬性和方法都可以使用。
我在IE中使用addEventListener()並沒有遇到問題,但是網上有一些開發者在使用時會遇到問題,提供的解決方法有數種:
https://social.msdn.microsoft…
第一種是加入一些指令碼:
<script language=`javascript` for="testAxEvent" event="testEvent(szType, szValue)">
// Test 1 - statically load the script (This is the basis for the hack)
// Works on IE8, IE9, IE10 and IE11
var MSG = document.getElementById("msg");
MSG.innerHTML = szType + " : " + szValue;
</script>
第二種是把IE的internet選項– >安全,點選“將所有區域重置為預設級別”。
3 跨瀏覽器的事件物件EventUtil
var EventUtil = {
//是否支援DOM2?是否是IE?是否支援DOM0?一般前兩個if包含了所有情況
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;
}
},
//IE沒有event物件,因此使用window.event
getEvent: function(event){
return event ? event : window.event;
},
/*使用getEvent()方法得到event物件後才可以使用getTarget()、
preventDefault()、stopPropagation()方法
*/
//先檢測DOM0 DOM2的target屬性,然後檢測IE的srcElement屬性
getTarget: function(event){
return event.target || event.srcElement;
},
//先檢測DOM0 DOM2的preventDefault(),再檢測IE的returnValue
preventDefault: function(event){
if (event.preventDefault){
event.preventDefault();
} else {
event.returnValue = false;
}
},
//是否支援DOM2?是否是IE?是否支援DOM0?一般前兩個if包含了所有情況
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;
}
},
//先檢測DOM0 DOM2的stopPropagation(),再檢測IE的cancelBubble
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
測試例項:
var but = document.getElementById("myButtun");
var handler = function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
alert(target);// [object HTMLInputElement]
};
EventUtil.addHandler(but, "click", handler);
當然,上述跨瀏覽器的事件物件所包含的屬性和方法並不是必須的,在實際中可以根據實際情況適當刪減一些屬性和方法。但是,萬變不離其宗。