跨浏览器的事件处理程序
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){ //**Dom2级事件处理程序**
element.addEventListener(type, handler, false);
} else if (element.attachEvent){ //**IE事件处理程序**
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;
}
}
};
在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");
});
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);
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
//这里省略了其他代码
btn.removeEventListener("click", handler, false); //有效!
在事件处理程序内部,对象this 始终等于currentTarget 的值,而target 则只包含事件的实
际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget 和target 包含相同的值。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.currentTarget === this); //true
alert(event.target === this); //true
};
这个例子检测了currentTarget
和target 与this
的值。由于click 事件的目标是按钮,
因此这三个值是相等的。如果事件处理程序存在于按钮的父节点中(例如document.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
,在那里事件才得到了处理。
在需要通过一个函数处理多个事件时,可以使用type 属性。例如:
var btn = document.getElementById("myBtn");
var handler = function(event){
switch(event.type){
case "click":
alert("Clicked");
break;
case "mouseover":
event.target.style.backgroundColor = "red";
break;
case "mouseout":
event.target.style.backgroundColor = "";
break;
}
};
btn.onclick = handler;
btn.onmouseover = handler;
btn. onmouseout = handler;
阻止特定事件的默认行为
可以使用preventDefault()方法。例如,链接的默认行为就是在被单击时会导航到其href 特性指定的URL。如果你想阻止链接导航这一默认行为,那么通过链接的onclick 事件处理程序可以取消它,如下面的例子所示。
var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
};
只有cancelable 属性设置为true 的事件,才可以使用preventDefault()来取消其默认行为
另外,stopPropagation()
方法用于立即停止事件在DOM 层次中的传播,即取消进一步的事件捕获或冒泡。例如,直接添加到一个按钮的事件处理程序可以调用stopPropagation(),从而避免触发注册在document.body 上面的事件处理程序,如下面的例子所示。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("Clicked");
event.stopPropagation();
};
document.body.onclick = function(event){
alert("Body clicked");
};
事件对象的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
}, true);
document.body.onclick = function(event){
alert(event.eventPhase); //3
};
当单击这个例子中的按钮时,首先执行的事件处理程序是在捕获阶段触发的添加到document.body中的那一个,结果会弹出一个警告框显示表示eventPhase 的1。接着,会触发在按钮上注册的事件处理程序,此时的eventPhase 值为2。最后一个被触发的事件处理程序,是在冒泡阶段执行的添加到document.body 上的那一个,显示eventPhase 的值为3。而当eventPhase 等于2 时,this、target和currentTarget 始终都是相等的。
**
只有在事件处理程序执行期间,event 对象才会存在;一旦事件处理程序执行完 成,event 对象就会被销毁。
**
IE中的事件对象
与访问DOM中的event 对象不同,要访问IE 中的event 对象有几种不同的方式,取决于指定事件处理程序的方法。在使用DOM0 级方法添加事件处理程序时,event 对象作为window 对象的一个属性存在。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
var event = window.event;
alert(event.type); //"click"
};
在此,我们通过window.event 取得了event 对象,并检测了被触发事件的类型(IE 中的type
属性与DOM中的type 属性是相同的)。可是,如果事件处理程序是使用attachEvent()添加的,那么就会有一个event 对象作为参数被传入事件处理程序函数中,如下所示
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(event){
alert(event.type); //"click"
});
因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this 会始终等于事件目标。故而,最好还是使用event.srcElement
比较保险。例如:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(window.event.srcElement === this); //true
};
btn.attachEvent("onclick", function(event){
alert(event.srcElement === this); //false
});
在第一个事件处理程序中(使用DOM0 级方法指定的),srcElement
属性等于this
,但在第二个事件处理程序中,这两者的值不相同。如前所述,returnValue
属性相当于DOM 中的preventDefault()
方法,它们的作用都是取消给定事件的默认行为。只要将returnValue
设置为false,就可以阻止默认行为。来看下面的例子。
var link = document.getElementById("myLink");
link.onclick = function(){
window.event.returnValue = false;
};
这个例子在onclick 事件处理程序中使用returnValue 达到了阻止链接默认行为的目的。与DOM不同的是,在此没有办法确定事件是否能被取消。
相应地,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");
};
通过在onclick 事件处理程序中将cancelBubble 设置为true,就可阻止事件通过冒泡而触发
document.body 中注册的事件处理程序。结果,在单击按钮之后,只会显示一个警告框。
跨浏览器的事件对象
虽然DOM 和IE 中的event 对象不同,但基于它们之间的相似性依旧可以拿出跨浏览器的方案来。IE 中event 对象的全部信息和方法DOM 对象中都有,只不过实现方式不一样。不过,这种对应关系让实现两种事件模型之间的映射非常容易。可以对前面介绍的EventUtil 对象加以增强,添加如下方法以求同存异。
var EventUtil = {
addHandler: function(element, type, handler){
//省略的代码
},
getEvent: function(event){
return event ? event : window.event;
},
getTarget: function(event){
return event.target || event.srcElement;
},
preventDefault: function(event){
if (event.preventDefault){
event.preventDefault();
} else {
event.returnValue = false;
}
},
removeHandler: function(element, type, handler){
//省略的代码
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
第二个方法是getTarget(),它返回事件的目标。在这个方法内部,会检测event 对象的target
属性,如果存在则返回该属性的值;否则,返回srcElement 属性的值。可以像下面这样使用这个方法。
btn.onclick = function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
};
第三个方法是preventDefault(),用于取消事件的默认行为。在传入event 对象后,这个方法
会检查是否存在preventDefault()方法,如果存在则调用该方法。如果preventDefault()方法不
存在,则将returnValue 设置为false。下面是使用这个方法的例子。
var link = document.getElementById("myLink");
link.onclick = function(event){
event = EventUtil.getEvent(event);
EventUtil.preventDefault(event);
};
以上代码可以确保在所有浏览器中单击该链接都不会打开另一个页面。首先,使用EventUtil.getEvent()取得event 对象,然后将其传入到EventUtil.preventDefault()以取消默认行为。
第四个方法是stopPropagation(),其实现方式类似。首先尝试使用DOM 方法阻止事件流,否则就使用cancelBubble
下面看一个例子
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("Clicked");
event = EventUtil.getEvent(event);
EventUtil.stopPropagation(event);
};
document.body.onclick = function(event){
alert("Body clicked");
};
在此,首先使用EventUtil.getEvent()取得了event 对象,然后又将其传入到EventUtil.
stopPropagation()。别忘了由于IE 不支持事件捕获,因此这个方法在跨浏览器的情况下,也只能用来阻止事件冒泡。
终极版本如下:
var EventUtil = {
// 跨浏览器注册事件
addHandler: function(element, type, handler){
if (element.addEventListener){ //**Dom2级事件处理程序**
element.addEventListener(type, handler, false);
} else if (element.attachEvent){ //**IE事件处理程序**
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;
}
}
//这里开始是事件对象跨浏览器
getEvent: function(event){
return event ? event : window.event;
},
getTarget: function(event){
return event.target || event.srcElement;
},
preventDefault: function(event){
if (event.preventDefault){
event.preventDefault();
} else {
event.returnValue = false;
}
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
};