JS之事件绑定(addEventListener、removeEventListener、attachEvent、detachEvent的用法和区别)

一. window.onload

1. 后面的onload覆盖前面的onload

在讲事件绑定之前,先来看看一个例子,我们知道onload事件,那么一个页面同时出现多个onload会出现什么情况呢,先来看看例子:

window.onload = function(){
var aBut = document.getElementById('but');
aBut.onclick  = function(){
alert('first onload!')
}
}
window.onload = function(){
var aBut = document.getElementById('but');
aBut.onclick  = function(){
alert('second onload!')
}
}

如果从正常情况下,得出的结果应该是 first onload!和second onload!结果如下:

当两组程序或两个 JS 文件同时执行的时候,后面一个会把前面一个完全覆盖掉。导致前面的 window.onload 完全失效了。那么怎么做才能前后都执行不被覆盖?

2. 解决onload覆盖问题

分析:1. 需要创建一个保存器保存之前执行的事件

    2. 再执行后面的事件之前,先判断保存器里面是否有之前的事件,如果有执行,再执行后面的事件

程序:

window.onload = function () { //第一个要执行的事件,会被覆盖
// alert('first onload!');
console.log('first onload!');
};
if (typeof window.onload == 'function') { //判断之前是否有 window.onload
var saved = null; //创建一个保存器
saved = window.onload; //把之前的 window.onload 保存起来
}
window.onload = function () { //最终一个要执行事件
if (saved) saved(); //执行之前一个事件
// alert('second onload!'); //执行本事件的代码
console.log('second onload!');
};

为了方便看到后面的没有被前面覆盖,使用console.log(),看结果两个都执行了

3. 自定义事件处理函数

如果页面有若干个onload事件,是不是在每一个事件前都需要判断一次呢,这样是可以,但是不觉得很烦吗,因此我们需要自定义方法把它封装起来,如下:

//自定义方法,解决覆盖问题、可读性问题、this 传递问题
function addEvent(obj, type, fn) { //取代传统事件处理函数
var saved = null; //保存每次触发的事件处理函数
if (typeof obj['on' + type] == 'function') { //判断是不是事件
saved = obj['on' + type]; //如果有,保存起来
}
obj['on' + type] = function () { //然后执行
if (saved) saved(); //执行上一个
fn.call(this); //执行函数,把 this 传递过去
};
}
//用在onload上
addEvent(window, 'load', function(){
console.log('first onload!');


});
addEvent(window, 'load', function(){
console.log('second onload!');
});

结果:

看结果是没有问题了,可是真的没问题吗,如果我做一个切换器,一直点击切换会发生什么情况呢?

//自定义方法,解决覆盖问题、可读性问题、this 传递问题
function addEvent(obj, type, fn) { //取代传统事件处理函数
var saved = null; //保存每次触发的事件处理函数
if (typeof obj['on' + type] == 'function') { //判断是不是事件
saved = obj['on' + type]; //如果有,保存起来
}
obj['on' + type] = function () { //然后执行
if (saved) saved(); //执行上一个
fn.call(this); //执行函数,把 this 传递过去
};
}
//用在切换器上
addEvent(window, 'load', function(){
var aBut = document.getElementById('but');
addEvent(aBut, 'click', toRed);


});
function toRed(){
this.className = 'red';
addEvent(this, 'click', toBlue);
}
function toBlue(){
this.className = 'blue';
addEvent(this, 'click', toRed);
}

当你单击很多很多次切换后,浏览器直接卡死,或者弹出一个错误:too much
recursion(太多的递归)。主要的原因是,每次切换事件的时候,都保存下来,没有把无用的
移除,导致越积越多,最后卡死。
如下:

到这儿看来,还需要把之前执行过的事件删除在执行下面的事件,自定义删除事件如下:

function removeEvent(obj, type){
if(obj['on'+ type])obj['on'+ type] = null;
}

以上的删除事件处理函数只不过是一刀切的删除了,这样虽然解决了卡死和太多递归的
问题。但其他的事件处理函数也一并被删除了,导致最后得不到自己想要的结果。如果想要
只删除指定的函数中的事件处理函数,那就需要遍历,查找。

二. DOM2定义的方法

1. addEventListener()和 removeEventListener()

这样是不是感觉很烦,下面我们看看“DOM2”级定义的两个方法,addEventListener()和 removeEventListener()。所有 DOM 节点中都包含这两个方法,并且它
们都接受 3 个参数;事件名、函数、冒泡或捕获的布尔值(true 表示捕获,false 表示冒泡)。我们使用它再来做一个切换器

window.addEventListener('load', function(){
var aBut = document.getElementById('but');
aBut.addEventListener('click', function(){
alert('不会被误删!');//不会被误删
}, false);
aBut.addEventListener('click', toRed, false);//引入切换不会
造成太多递归被卡死
}, false);
function toRed(){
this.className = 'red';
this.removeEventListener('click', toRed, false);
this.addEventListener('click', toBlue, false);
}
function toBlue(){
this.className = 'blue';
this.removeEventListener('click', toBlue, false);
this.addEventListener('click', toRed, false);
}

执行结果也没有问题,可是IE不支持。

2. attachEvent()和 detachEvent()

IE 实现了与 DOM 中类似的两个方法:attachEvent()和 detachEvent()。这两个方法接受
相同的参数:事件名称和函数。
在使用这两组函数的时候,先把区别说一下:1.IE 不支持捕获,只支持冒泡;2.IE 添加
事件不能屏蔽重复的函数;3.IE 中的 this 指向的是 window 而不是 DOM 对象。4.在传统事
件上,IE 是无法接受到 event 对象的,但使用了 attchEvent()却可以,但有些区别。

切换器:

window.attachEvent('onload', function(){
var aBut = document.getElementById('but');
aBut.attachEvent('onclick', function(){
alert('IE测试会不会被删除!');
});
aBut.attachEvent('onclick', toRed);
// aBut.attachEvent('onclick', function(){
// toRed.call(aBut);//强制指向aBut
// });
});
function toRed(){
var that = window.event.srcElement;//this指向的事window,因此需要指向目标元素
that.className = 'red';
that.detachEvent('onclick', toRed);
that.attachEvent('onclick', toBlue);
}
function toBlue (){
var that = window.event.srcElement;
that.className = 'blue';
that.detachEvent('onclick', toBlue);
that.attachEvent('onclick', toRed);
}

在这里需要注意,IE的事件绑定函数无法传递this,这儿的this指向的事window。

3. 兼容模式

这样做无论IE的标准模式,还是Quirks都能支持,但是IE可以了,其他的浏览器又不支持,所有需要做兼容,如下:

//绑定事件
function addEvent(obj, type, fn){
if(obj.addEventListener){
obj.addEventListener(type, fn)
}
if(obj.attachEvent){
obj.attachEvent('on'+ type, fn);
}
}
//删除事件
function removeEvent(obj, type, fn){
if(obj.removeEventListener){
obj.removeEventListener(type, fn);
}
if(obj.detachEvent){
obj.detachEvent('on'+ type, fn);
}
}
//得到目标事件
/*function getTarget(ev){//IE和W3C目标事件兼容,下面的方法也可以实现
if(ev.target){
return ev.target;
}else if(window.event.srcElement){
return window.event.srcElement;
}
}*/


addEvent(window, 'load', function(){
var aBut = document.getElementById('but');
addEvent(aBut, 'click', function(ev){
alert(ev.type);
});
addEvent(aBut, 'click', toRed);
});
function toRed(){
var that = (this == window) ? window.event.srcElement : this;//IE和W3C目标事件兼容
that.className = 'red';
removeEvent(that, 'click', toRed);
addEvent(that, 'click', toBlue);
}
function toBlue(){
var that = (this == window) ? window.event.srcElement : this;
that.className = 'blue';
removeEvent(that, 'click', toBlue);
addEvent(that, 'click', toRed);
}

IE 中的事件绑定函数 attachEvent()和 detachEvent()可能在实践中不去使用,有几个
原因:1.IE9 就将全面支持 W3C 中的事件绑定函数;2.IE 的事件绑定函数无法传递 this;3.IE
的事件绑定函数不支持捕获;4.同一个函数注册绑定后,没有屏蔽掉;5.有内存泄漏的问题 

这些问题以后再去讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值