问题所在:
1 支持同一个元素的同一个事件句柄上可以绑定多个坚挺函数;
2 如果在同一个元素的同一个事件句柄上多次注册同一个函数,那么除第一次注册的函数,其余都将被忽略;
3 函数体内的this指向的应当是正在处理事件的节点(IE指向window);
4 监听函数的执行顺序应当是按照绑定的顺序执行;
5 在函数体内不用使用 event = event || window.event,来标准化Event对象。
var btn = document.getElementById("btn"); btn.onclick = function () { alert('我是传统事件绑定'); }
以上代码展示的传统的事件绑定。现代绑定中W3C使用的是:addEventListener和removeEventListener.IE使用的是attachEvent和detachEvent.
// 跨浏览器添加事件
function addEvent(obj, type, fn) {
if (typeof addEventListener != 'undefined')
{
obj.addEventListener(type, fn, false);
} else if (typeof attachEvent != 'undefined')
{
obj.attachEvent('on' + type, fn);
}
}```
//跨浏览器删除事件
function removeEvent(obj, type, fn) {
if (typeof removeEventListener != 'undefined')
{
obj.removeEventListener(type, fn);
} else if (typeof detachEvent != 'undefined')
{
obj.detachEvent('on' + type, fn);
}
}
以上的两个函数解决了:***同时绑定多个函数以及标准化event的问题***。但是*IE多次注册同一函数*,第一个除外的其他函数并未被忽略;IE中监听函数的执行并不是按照绑定的顺序进行执行,而是*倒序执行*。还有一个问题就是IE中*this传递过来的是window*,而不是当前正在运行事件句柄的节点。
为了解决this传递问题,我们需要使用匿名函数+传递参数的方式来解决:
` obj.addEvent('on' + type,function(){
fn.call(obj,window.event);
});`
使用call第一个参数就是获取this,从第二个参数开始,可以通过函数参数获取。故以上代码解决了IE中this指向window的问题,同时可以获取到event对象。
但是这又引入了新的问题:***无法删除事件 无法顺序执行 IE的现代绑定存在内从泄露的问题。***
从上述问题我们可以看到主要问题是在IE的attachEvent上,存在着无法避免地内佛那个泄露问题。所以我们考虑使用传统事件绑定对IE进行封装。
//跨浏览器添加事件绑定
function addEvent(obj, type, fn) {
if (typeof obj.addEventListener != 'undefined') {
obj.addEventListener(type, fn, false);
} else {
//创建一个可以保存事件的哈希表(散列表)
if (!obj.events) obj.events = {};
if (!obj.events[type]) {
//创建一个可以保存事件处理函数的数组
obj.events[type] = [];
//存储第一个事件处理函数
if (obj['on' + type]) obj.events[type][0] = fn;
}
//通过事件计数器来从第二个事件处理函数开始
obj.events[type][addEvent.ID++] = fn;
//执行所有事件处理函数
obj['on' + type] = function () {
for (var i in obj.events[type]) {
obj.events[type][i]();
}
}
}
}
//每个事件分配一个ID 计数器
addEvent.ID = 1;
//事件处理函数调用
addEvent.exec = function (event) {
var e = event || addEvent.fixEvent(window.event);
var es = this.events[e.type];
for (var i in es) {
es[i].call(this, e);
}
};
//获取IE 的event,兼容W3C 的调用
addEvent.fixEvent = function (event) {
event.preventDefault = addEvent.fixEvent.preventDefault;
event.stopPropagation = addEvent.fixEvent.stopPropagation;
return event;
};
//兼容IE 和W3C 阻止默认行为
addEvent.fixEvent.preventDefault = function () {
this.returnValue = false;
};
//兼容IE 和W3C 取消冒泡
addEvent.fixEvent.stopPropagation = function () {
this.cancelBubble = true;
};
//跨浏览器删除事件
function removeEvent(obj, type, fn) {
if (typeof obj.removeEventListener != 'undefined') {
obj.removeEventListener(type, fn, false);
} else {
var es = obj.events[type];
for (var i in es) {
if (es[i] == fn) {
delete obj.events[type][i];
}
}
}
}
以上就是基于原声JS自己封装的事件绑定库。后续的话需要研究下jQuery的源码,看看关于事件绑定这部分的代码。