Event 實例



在事件發生時,會有個Event實例收集事件的相關資訊,不幸的是,這個實例的取得方式在標準瀏覽器與Internet Explorer上有所不同。在標準瀏覽器上,Event實例會作為事件處理器的的第一個參數,在Internet Explorer上,則是被window.event參考。所以為了處理跨瀏覽器的差異,常看到這樣的程式碼:
function handler(event) {
    event = event || window.event;
    ...
}

如果event作為第一個參數,則直接使用,否則就使用window.event。Event的差異性不僅如此,Event上的一些特性,也存在著一些差異性。例如,若要取得操作的目標物件,在標準瀏覽器上,要使用Event實例的target特性,在Internet Explorer上,要使用Event實例的srcElement特性。

那麼何為操作的目標物件,如果你在按鈕上點選,那麼按鈕就是操作的目標物件,在
基本事件模型 中有說明過,觸發事件時,事件處理器的this會設定為當時的元素,那麼為何還要有特性指出操作目標物件?

事實上,在基本事件模型中,操作時若發生事件,並事件不僅停於操作的元素,還會從操作的元素往外傳播,若外層元素亦有設定對應的事件處理器,亦會呼叫事件處理器。這可以用下面的範例來示範:
<html>
<head>
<title>Event-1</title>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<script type="text/javascript">
window.onload = function() {
function handler(event) {
event = event || window.event;
var target = event.target || event.srcElement;
document.getElementById('console').innerHTML +=
'<br><b>this.id:</b> ' + this.id +
', <b>target.id:</b> ' + target.id;
}
document.getElementById('bodyId').onclick = handler;
document.getElementById('divId').onclick = handler;
document.getElementById('btnId').onclick = handler;
};
</script>
</head>
<body id="bodyId">
<div id="divId">
<button id="btnId">按我</button>
</div>
<span id="console"></span>
</body>
</html>

在上例中,使用了物件偵測,讓Event實例與目標元素的取得可以跨瀏覽器。按鈕是包括在<div>中,而<body>是<div>的外層元素,三者都設定了事件處理器。如果你試著按下按鈕,則會看到結果如下:
this.id: btnId, target.id: btnId
this.id: divId, target.id: btnId
this.id: bodyId, target.id: btnId

不僅按鈕的事件處理器被呼叫,外層<div>與<body>也依序被呼叫,這樣的行為叫作事件氣泡傳播(Event Bubbling)。事件傳播至元素並呼叫事件處理器時,this就設定為該元素,這可以從this.id的顯示結果觀察到,並注意到,由於操作時按下的是按鈕,所以操作目標元素就是按鈕,這可以由target.id觀察到。

事件氣泡傳播可以善用。例如在 修改文件 中動態新增圖片的例子,每建立一個新的<img>,就設定該<img>的click事件處理器,以便在點選圖片時自動移除圖片。若利用事件氣泡傳播,則可以在<div>上設定一次事件處理器,完成相同的結果。例如:
<html>
<head>
<title>Event-2</title>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<script type="text/javascript">
window.onload = function() {
var images = document.getElementById('images');
images.onclick = function(event) {
event = event || window.event;
var target = event.target || event.srcElement;
this.removeChild(target);
};
document.getElementById('add').onclick = function() {
var img = document.createElement('img');
img.src = document.getElementById('src').value;
images.appendChild(img);
};
};
</script>
</head>
<body>
<input id="src" type="text"><button id="add">新增圖片</button>
<div id="images"></div>
</body>
</html>

如果你想要停止事件傳播,方式也存在著瀏覽器的差異,在遵守標準的瀏覽器上,必須呼叫Event的stopPropagation()方法,在Internet Explorer上,則要將Event的cancelBubble設為false。例如要將第一個範例停止目標元素外的事件傳播,可以如下:
<html>
<head>
<title>Event-3</title>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<script type="text/javascript">
window.onload = function() {
function handler(event) {
event = event || window.event;
var target = event.target || event.srcElement;
document.getElementById('console').innerHTML +=
'<br><b>this.id:</b> ' + this.id +
', <b>target.id:</b> ' + target.id;
if(event.stopPropagation) {
event.stopPropagation();
}
else {
event.cancelBubble = true;
}
}
document.getElementById('bodyId').onclick = handler;
document.getElementById('divId').onclick = handler;
document.getElementById('btnId').onclick = handler;
};
</script>
</head>
<body id="bodyId">
<div id="divId"><button id="btnId">按我</button></div>
<span id="console"></span>
</body>
</html>

Event實例在不同的瀏覽器中有一些特性是不相容的,這在之後介紹若有機會再提,這類的問題,通常會撰寫程式庫來封裝差異性,而跨瀏覽器執行也是許多著名程式庫的主要目標之一。