上一次,了解了JS自定義事件隔节。今天在DOM上進(jìn)行事件方法擴(kuò)展。
1.基于DOM擴(kuò)展自定義方法(了解即可)
我們一起來(lái)添加一個(gè)addEvent方法
if (window.HTMLElement) {
// 使用原型擴(kuò)展DOM自定義事件
HTMLElement.prototype.addEvent = function(type, fn, capture) {
var el = this;
if (window.addEventListener) {
el.addEventListener(type, function(e) {
fn.call(el, e);
}, capture);
} else if (window.attachEvent) {
el.attachEvent("on" + type, function(e) {
fn.call(el, e);
});
}
};
} else {
// 如果是不支持HTMLElement擴(kuò)展的瀏覽器
// 通過(guò)遍歷所有元素?cái)U(kuò)展DOM事件
var elAll = document.all, lenAll = elAll.length;
for (var iAll=0; iAll<lenAll; iAll+=1) {
elAll[iAll].addEvent = function(type, fn) {
var el = this;
el.attachEvent("on" + type, function(e) {
fn.call(el, e);
});
};
}
}
HTMLElement 接口表示所有的 HTML 元素(nodeType==1)。以一個(gè)<p>標(biāo)簽元素舉例奉芦,其向上尋找原型對(duì)象用過(guò)會(huì)是這樣:HTMLParagraphElement.prototype → HTMLElement.prototype → Element.prototype → Node.prototype → Object.prototype → null。上述代碼HTMLElement直接換成Element也是可以的剧蹂,但是會(huì)讓其他元素(例如文本元素等)也擴(kuò)展addEvent方法声功,有些浪費(fèi)了。
通過(guò)上面的擴(kuò)展宠叼,element上就有了addEvent()方法先巴。我們可以像下面展示的那樣使用
<div id="content-wrap" class="content-wrap">這是pattyzzh的領(lǐng)地</div>
document.getElementById("content-wrap").addEvent("click", function() {
alert("歡迎光臨pattyzzh");
});
基于DOM擴(kuò)展缺點(diǎn)有:缺少標(biāo)準(zhǔn)無(wú)規(guī)律、提高沖突可能性冒冬、性能以及瀏覽器支持伸蚯。擴(kuò)展名字任意命,很有可能就會(huì)與未來(lái)DOM瀏覽器本身支持的方法相互沖突简烤;擴(kuò)展無(wú)規(guī)律剂邮,很有可能出現(xiàn)A和B同名不同功能的擴(kuò)展而造成沖突;IE6-7瀏覽器下所有擴(kuò)展都要通過(guò)遍歷支持横侦,其性能開(kāi)銷可想而知挥萌;另外IE8對(duì)DOM擴(kuò)展的支持并不完整,例如其支持Element.prototype枉侧,卻沒(méi)有HTMLElement.prototype.
2.偽DOM自定義事件
這里的“偽DOM自定義事件”是自己定義的一個(gè)名詞引瀑,用來(lái)區(qū)分DOM自定義事件的。例如jQuery庫(kù)榨馁,其是基于包裝器(一個(gè)包含DOM元素的中間層)擴(kuò)展事件的憨栽,既與DOM相關(guān),又不直接是DOM翼虫,因此屑柔,稱之為“偽DOM自定義事件”悴晰。
如果只考慮事件添加笛丙,我們的工作其實(shí)很簡(jiǎn)單,根據(jù)支持情況馅精,addEventListener與attachEvent方法分別添加事件即可:
addEvent: function(type, fn, capture) {
var el = this.el;
if (window.addEventListener) {
el.addEventListener(type, fn, capture);
} else if (window.attachEvent) {
el.attachEvent("on" + type, fn);
}
return this;
}
自定義事件添加容易次慢,但是如何觸發(fā)它們呢旁涤?——考慮到自定義事件與瀏覽器行為無(wú)關(guān)翔曲,同時(shí)瀏覽器沒(méi)有直接的觸發(fā)事件的方法。
自定義事件的觸發(fā)
- 對(duì)于標(biāo)準(zhǔn)瀏覽器劈愚,其提供了可供元素觸發(fā)的方法:element.dispatchEvent()瞳遍。不過(guò),在使用該方法之前菌羽,我們還需要做其他兩件事掠械,及創(chuàng)建和初始化。因此注祖,總結(jié)說(shuō)來(lái)就是:
document.createEvent()
event.initEvent()
element.dispatchEvent()
createEvent()方法返回新創(chuàng)建的Event對(duì)象猾蒂,支持一個(gè)參數(shù),表示事件類型
initEvent()方法用于初始化通過(guò)DocumentEvent接口創(chuàng)建的Event的值是晨。支持三個(gè)參數(shù):initEvent(eventName, canBubble, preventDefault). 分別表示事件名稱肚菠,是否可以冒泡,是否阻止事件的默認(rèn)操作罩缴。
dispatchEvent(eventObj)就是觸發(fā)執(zhí)行了.
舉個(gè)例子
$(dom).addEvent("sayHello", function() {
alert("Hello");
});
// 創(chuàng)建
var event = document.createEvent("HTMLEvents");
// 初始化
event.initEvent("sayHello",true, false);
// 觸發(fā), 即彈出文字
dom.dispatchEvent(event);
- 對(duì)于IE瀏覽器蚊逢,由于向下很多版本的瀏覽器都不支持document.createEvent()方法)。IE瀏覽器有不少自給自足的東西箫章,例如下面要說(shuō)的這個(gè)"propertychange"事件烙荷,顧名思義,就是屬性改變即觸發(fā)的事件檬寂。例如文本框value值改變终抽,或是元素id改變,或是綁定的事件改變等等焰薄。
當(dāng)我們添加自定義事件的時(shí)候拿诸,順便給元素添加一個(gè)自定義屬性即可。例如塞茅,我們添加自定義名為"sayHello"的自定義事件,順便我們可以對(duì)元素做點(diǎn)小手腳:
dom.listener = 0;
再順便把自定義事件fn塞到"propertychange"事件中:
dom.attachEvent("onpropertychange", function(e) {
if (e.propertyName == "listener") {
fn.call(this); //fn是事件處理函數(shù)
}
});
這個(gè)季率,當(dāng)我們需要觸發(fā)自定義事件的時(shí)候野瘦,只要修改DOM上自定義的listener屬性的值即可:
dom.listener = Math.random(); // 值變成隨機(jī)數(shù)
此時(shí)就會(huì)觸發(fā)dom上綁定的onpropertychange事件,又因?yàn)樾薷牡膶傩悦檬?listener", 于是自定義的fn就會(huì)被執(zhí)行飒泻。這就是IE瀏覽器下事件觸發(fā)實(shí)現(xiàn)的完整機(jī)制鞭光。
自定義事件的刪除
與觸發(fā)事件不同,事件刪除泞遗,各個(gè)瀏覽器都提供了對(duì)于的時(shí)間刪除方法惰许,如removeEventListener和detachEvent。不過(guò)呢史辙,對(duì)于IE瀏覽器汹买,還要多刪除一個(gè)事件佩伤,就是為了實(shí)現(xiàn)觸發(fā)功能額外增加的onpropertychange事件:
dom.detachEvent("onpropertychange", event);
綜合
var $ = function(el) {
return new _$(el);
};
var _$ = function(el) {
this.el = (el && el.nodeType == 1)? el: document;
};
_$.prototype = {
constructor: _$,
addEvent: function(type, fn, capture) {
var el = this.el;
if (window.addEventListener) {
el.addEventListener(type, fn, capture);
var ev = document.createEvent("HTMLEvents");
ev.initEvent(type, capture || false, false);
if (!el["ev" + type]) {
el["ev" + type] = ev; //將自定義事件存儲(chǔ)在該元素屬性下,觸發(fā)時(shí)使用
}
} else if (window.attachEvent) {
el.attachEvent("on" + type, fn);
if (isNaN(el["cu" + type])) {
// 自定義屬性晦毙,用來(lái)間接觸發(fā)觸發(fā)自定義事件
el["cu" + type] = 0;
}
var fnEv = function(event) {
if (event.propertyName == "cu" + type) { fn.call(el); }
};
el.attachEvent("onpropertychange", fnEv);
if (!el["ev" + type]) {
el["ev" + type] = [fnEv];
} else {
el["ev" + type].push(fnEv); //同一事件的多個(gè)處理函數(shù)
}
}
return this;
},
fireEvent: function(type) {
var el = this.el;
if (typeof type === "string") {
if (document.dispatchEvent) {
if (el["ev" + type]) {
el.dispatchEvent(el["ev" + type]);
}
} else if (document.attachEvent) {
el["cu" + type]++;
}
}
return this;
},
removeEvent: function(type, fn, capture) {
var el = this.el;
if (window.removeEventListener) {
el.removeEventListener(type, fn, capture || false);
} else if (document.attachEvent) {
el.detachEvent("on" + type, fn);
var arrEv = el["ev" + type];
if (arrEv instanceof Array) {
for (var i=0; i<arrEv.length; i+=1) {
//這里還需要再次過(guò)濾生巡,否則會(huì)刪除該事件所有處理函數(shù)(ie下)
//條件?這里提示:1.對(duì)象的比較是比較引用 2.el["ev" + type]里裝的fnEv而不是fn 這里就難辦了
el.detachEvent("onpropertychange", arrEv[i]);
}
}
}
return this;
}
};
測(cè)試
var fnClick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if (target.nodeType === 1) {
alert("點(diǎn)擊類型:" + e.type);
$(target).fireEvent("sayHello"); //觸發(fā)自定義事件
}
}, sayHello1 = function() {
alert("Hello 1");
}, sayHello2 = function() {
alert("Hello 2");
};
// 梅西圖片
var elImage = document.getElementById("myImg");
$(elImage)
.addEvent("click", fnClick)
.addEvent("sayHello", sayHello1)
.addEvent("sayHello", sayHello2);
// 刪除自定義事件按鈕
var elButton = document.getElementById("myBut");
$(elButton).addEvent("click", function() {
$(elImage)
.removeEvent("sayHello", sayHello1)
.removeEvent("sayHello", sayHello2);
alert("清除成功见妒!");
});
html:
<div class="box" id="box">
![](./meixi.jpg)
<input type="button" value="點(diǎn)擊清除自定義事件 " id="myBut">
</div>
運(yùn)行結(jié)果如下:
當(dāng)我們點(diǎn)擊圖片孤荣,會(huì)出現(xiàn)下面三個(gè)彈窗
當(dāng)我們點(diǎn)擊按鈕: