事件(1) 跨浏览器的事件处理程序总结(2016/06/28更新)
这块是前端比较重要的地方,需要深刻理解。我们说js与HTML之间的交互是通过事件实现的。页面接收事件是按照一个顺序的,也就是有相应的事件流,有意思的是,IE和其他浏览器的事件流正相反,IE是事件冒泡流,讲究从具体到一般;其他浏览器是事件捕获流,讲究从一般到具体。
上面这一段划掉的地方说的不对,不管是IE还是其他浏览器,事件触发的顺序都是从一般到具体,然后在从具体回溯到一般的,现代浏览器(其他+IE9及以上)利用addEventListener()监听事件,这个函数有三个参数,其中第三个参数默认是false,如果是true的话就是事件捕获的顺序了。为什么老版的IE只有事件冒泡呢,因为老版IE用的是attachEvent方法,这个方法只有两个参数,没得选。
对于事件处理程序来讲,目前有4种方式,其中HTML事件处理方式,就是直接在标签上绑个onclick之类的,因为缺点太多,尽量不用了。这里面多说一句,html的tag上绑事件这种方式对于事件属性名是不分大小写的,就是oncLICK,ONCLICK,你怎么写都可以的,但是js是区分大小写的。DOM0(不是标准,只是个时间点,IE4支持的DHTML)的方式比较简单,可以跨浏览器,但是不支持同时绑定多个事件处理程序,DOM2 IE8-不支持,IE也有一套自己的方式,我在下面都做了测试,例子的注释也比较详细。
对于事件对象,所有浏览器都有的属性包括: 1.判断event的类型;2.获取event的目标;3.阻止冒泡;4.阻止默认行为,当然IE和其他浏览器属性的获取方式也不一样的,下面同样进行了测试。
EventUtil是编写的可以跨浏览器的事件工具,可以直接用
//跨平台事件工具
var EventUtil = {
// 说明:只考虑DOM2方法和IE8-的方法即可包含现代全部浏览器
// 绑定事件
addEventFunction: function(element, type, funcName){
if ( element.addEventListener ) {
element.addEventListener( type, funcName, false );
}else{
element.attachEvent("on"+type, funcName );
};
},
//得到事件对象event
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;
};
},
//取消事件冒泡
stopBubble: function(event){
if (event.stopPropagation) {
event.stopPropagation();
}else{
event.cancelBubble = true;
};
},
// 解除事件
removeEventFunction: function(element, type, funcName){
if ( element.removeEventListener ) {
element.removeEventListener( type, funcName, false );
}else{
element.detachEvent("on"+type, funcName );
};
}
};
//绑定事件例子1
var div = document.getElementById("testDiv");
var testfunc = function(event){
//获取Event
var eventObj = EventUtil.getEvent(event);
//console.log(eventObj.type);
//获取事件目标
var target = EventUtil.getTarget(event);
//console.log(target);
alert("div");
//取消事件冒泡
EventUtil.stopBubble(event);
}
//绑定事件例子2
var a = document.getElementById("testa");
var testfunc2 = function(event){
//取消事件的默认行为
EventUtil.preventDefault(event);
alert("111");
}
//绑定事件例子2
var testfunc3 = function(event){
alert("body");
}
EventUtil.addEventFunction(div, "click", testfunc );
EventUtil.addEventFunction(a, "click", testfunc2 );
EventUtil.addEventFunction( document.body , "click", testfunc3 );
//////////////////////////////////////////////////////////////////////////
//事件处理程序
//DOM0 写法简单,跨浏览器的优势,不能添加多个事件处理程序,只执行最后一个
// var div = document.getElementById("testDiv");
// div.onclick = function(){
// alert("111");
// }
// div.onclick = null;
//HTML的tag事件绑定也具有上面这种就近覆盖的方式
//DOM2 IE8不支持,可以添加多个事件处理程序
// var div = document.getElementById("testDiv");
// div.addEventListener("click",function(){
// alert("222");
// }, false);
// div.addEventListener("click",function(){
// alert("333");
// }, false);
//如果解除绑定的话,函数不能写成匿名函数。
//类似:div.removeEventListener("click", 函数名, false );才有效
//IE事件处理程序,和DOM0的区别:会在全局运行,this==window,
//和DOM2的区别:可以添加多个事件处理程序,但是顺序和DOM2的是相反的。
//IE8及以下都是5,4;IE9,10是4,5;IE11不支持了
// var div = document.getElementById("testDiv");
// div.attachEvent("onclick",function(){
// alert("444");
// });
// div.attachEvent("onclick",function(){
// alert("555");
// });
//如果解除绑定的话,函数不能写成匿名函数。
//类似:div.detachEvent("onclick",handler);才有效
//add 2014-10-09
//现代浏览器DOM事件总结
//gt IE8
//监听事件
var listenButton = document.getElementById("listen");
function callback(){
alert("listen success!");
}
//你可以吧callback写成匿名函数的,
//但是如果你要以后移除的话,就不能写成匿名函数
listenButton.addEventListener("click",callback);
//事件移除
listenButton.removeEventListener("click",callback);
//维护函数上下文
var weihuButton = document.getElementById("weihu");
var user = {
name: 'sunny',
say: function(){
alert("my name is "+this.name);
}
};
//优雅的写法:这里bind一个新的函数,保持user上下文关系
user.say = user.say.bind(user);
//或者用匿名函数,比较丑
weihuButton.addEventListener("click",user.say);
//事件在DOM中的流动过程包括IE9+
//DOM事件被触发的时候会经历三个阶段。
//1.从文档的根节点流向目标对象(捕获阶段)
//2.在目标对象上被触发(目标阶段)
//3.再回溯到文档的根节点(冒泡阶段)。
var outside = document.getElementById("outside");
var inside = document.getElementById("inside");
//开启捕获模式
outside.addEventListener("click",function(){
alert("outside-capture");
//用来阻止事件流中的后续事件
//event.stopPropagation();
//更猛烈的阻止同一个元素上绑定的其他事件
//event.stopImmediatePropagation();
//用来阻止浏览器默认事件,比如a
//event.preventDefault();
},true);
inside.addEventListener("click",function(){
alert("inside-capture");
},true);
//开启冒泡模式
inside.addEventListener("click",function(){
alert("inside-bubble");
},false);
outside.addEventListener("click",function(){
alert("outside-bubble");
},false);
在最后补充两点:
1.在回掉函数中return false;等于stopPropagation()加上preventDefault();
2.event.target == $(this)用来判断是否是自身,这个方法我在obj见到同事calvin用过,那个场景用的不太合适,改成了event.target.tagName=="a",来解决了,但是这仍然是一个判断是否是自身的好方法。
//eg:
$(".test").click(function(event){
if ( event.target == $(this) ) {
...
}
});