問題所在:
1 支持同一個元素的同一個事件句柄上可以綁定多個堅挺函數;
2 如果在同一個元素的同一個事件句柄上多次注冊同一個函數,那么除第一次注冊的函數,其余都將被忽略;
3 函數體內的this指向的應當是正在處理事件的節點(IE指向window);
4 監聽函數的執行順序應當是按照綁定的順序執行;
5 在函數體內不用使用 event = event || window.event,來標準化Event對象。
var btn = document.getElementById("btn"); btn.onclick = function () { alert('我是傳統事件綁定'); }
以上代碼展示的傳統的事件綁定?,F代綁定中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的源碼,看看關于事件綁定這部分的代碼。