之前寫了篇文章《原生javascript實現類似jquery on方法的行為監聽》比較淺顯,能夠簡單的使用場景。
這里的自定義事件指的是區別javascript默認的與DOM交互的事件,比如click,mouseover,change等,有時候我們需要監聽某一行為是否發生,很顯然默認的行為不夠用,比如一個場景。我們寫好了tab切換,點擊后請求加載隱藏標簽的內容。
tab切換是非常常用的一個功能,通常會寫成組件,如果每次把請求寫在組件里肯定對組件拓展和耦合性有影響。這時候我們可以在組件里自定義一個事件廣播,在其他地方監聽這個廣播再執行請求就可以避免了。
以上只是使用場景,下面是具體實現:
1、簡易代碼,基礎功能:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>自定義事件</title> </head> <body> <button id="demo">點我吧</button> <script type="text/javascript"> function Observer(){ this._events={}; } Observer.prototype={ on:function(eName,fn,scope){ eName=eName.toLowerCase(); this._events[eName]=[]; this._events[eName].push({fn:fn||null,scope:scope||null}); }, fireEvent:function(){ var args=Array.prototype.slice.call(arguments);//將參數轉為數組 var eName=args.shift().toLowerCase(); var list=this._events[eName]; for(var i=0;i<list.length;i++){ var dict=list[i]; var fn=dict.fn; var scope=dict.scope; fn.apply(scope||null,args);//注冊事件執行 } } } var listener=new Observer(); listener.on("alert",function(name ,age){ console.log(name+":"+age); }); listener.on("aha",function(name ,age){ console.log("這是另外一個事件"+name+":"+age); }); var $btn=document.getElementById("demo") $btn.onclick=function(){ listener.fireEvent('aha', '彼岸花再開', 28); } </script> </body> </html>
有點類似jquery里面的on方法,$(ele).on(type,fn)。上面的on是注冊一個事件,這個事件是一個對象,存儲當前的事件名稱和函數。fireEvent可以理解為釋放,發射,監聽事件。
完整的實現:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定義事件</title>
</head>
<body>
<button id="demo">點我吧</button>
<script type="text/javascript">
function Observer() {
this._events = {};
}
Observer.prototype = {
constructor: this,
addEvent: function(eName, fn) {
if (typeof(eName) === "string" && typeof(fn) === "function") {
eName = eName.toLowerCase();
if (typeof(this._events[eName]) === "undefined") {
this._events[eName] = [fn];
} else {
this._events[eName].push(fn);
}
}
return this;
},
addEvents: function(obj) { //綁定多事件
obj = typeof(obj) === "object" ? obj : {};
for (var eName in obj) {
if (eName && typeof(obj[eName] === "function")) {
this.addEvent(eName, obj[eName]);
}
}
return this;
},
fireEvent: function(eName) { //觸發事件
if (eName && this._events[eName]) {
var events = {
eName: eName,
target: this
};
for (var length = this._events[eName].length, start = 0; start < length; start++) {
this._events[eName][start].call(this, events);
}
}
return this;
},
fireEvents: function(array) {
if (array instanceof Array) {
for (var i = 0, len = array.length; i < len; i++) {
this.fireEvent(array[i]);
}
}
return this;
},
removeEvent: function(eName, key) { //刪除綁定的事件
var eventsList = this._events[eName];
if (eventsList instanceof Array) {
if (typeof(key) === "function") {
for (var i = 0, len = eventsList.length; i < len; i++) {
if (eventsList[i] === key) {//移除其中某個事件
eventsList.splice(i, 1);
break;
}
}
} else if (key instanceof Array) {//移除某個事件下面多個函數
for (var lis = 0, lenkey = key.length; lis < lenkey; lis += 1) {
this.removeEvent(type, key[lenkey]);
}
} else {//直接移除事件下所有函數
delete this._events[eName];
}
}
return this;
},
removeEvents: function(params) {
if (params instanceof Array) {
for (var i = 0, length = params.length; i < length; i += 1) {
this.removeEvent(params[i]);
}
} else if (typeof params === "object") {
for (var type in params) {
this.removeEvent(type, params[type]);
}
}
return this;
}
}
var listeners = new Observer();
listeners.addEvents({
"once": function() {
alert("該事件只會出現一次!");
this.removeEvent("once");
},
"infinity": function() {
alert("每次點擊頁面,該事件都會!");
}
});
document.onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if (!target || !/button/i.test(target.tagName)) {
listeners.fireEvents(["once", "infinity"]);
}
};
</script>
</body>
</html>
換apply實現,細心的你是否發現其中的差別呢:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定義事件</title>
</head>
<body>
<button id="demo">點我吧</button>
<script type="text/javascript">
function Observer() {
this._events = {};//存儲事件對象
}
Observer.prototype = {
constructor: this,
addEvent: function(eName, fn) {//綁定事件
if (typeof(eName) === "string" && typeof(fn) === "function") {
eName = eName.toLowerCase();
if (typeof(this._events[eName]) === "undefined") {
this._events[eName] = [{//將事件函數綁定到事件名稱
fn: fn
}];
} else {
this._events[eName].push({
fn: fn
});
}
}
return this;
},
addEvents: function(obj) { //綁定多事件
obj = typeof(obj) === "object" ? obj : {};
for (var eName in obj) {
if (eName && typeof(obj[eName] === "function")) {
this.addEvent(eName, obj[eName]);
}
}
return this;
},
fireEvent: function(eName) {//廣播事件,射吧!
var args = Array.prototype.slice.call(arguments);//將參數轉為數組
var eName = args.shift().toLowerCase();//第一個參數是事件名,這里需要除時間名之外的參數
var list = this._events[eName];
if(list instanceof Array){
for (var i = 0; i < list.length; i++) {
var dict = list[i];
var fn = dict.fn;
fn.apply(null, args);
}
}
return this;
},
fireEvents: function(array) {
if (array instanceof Array) {
for (var i = 0, len = array.length; i < len; i++) {
this.fireEvent(array[i]);
}
}
return this;
},
removeEvent: function(eName, key) {
var eventsList = this._events[eName];
if (eventsList instanceof Array) {
if (typeof(key) === "function") {
for (var i = 0, len = eventsList.length; i < len; i++) {
if (eventsList[i] === key) {
eventsList.splice(i, 1);
break;
}
}
} else if (key instanceof Array) {
for (var lis = 0, lenkey = key.length; lis < lenkey; lis += 1) {
this.removeEvent(type, key[lenkey]);
}
}else{
delete this._events[eName];
}
}
}
}
var listeners = new Observer();
listeners.addEvents({
"once": function() {
alert("該事件只會出現一次!");
listeners.removeEvent("once");
},
"infinity": function() {
alert("每次點擊頁面,該事件都會出現!");
}
});
document.onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if (!target || !/input|pre/i.test(target.tagName)) {
listeners.fireEvents(["once", "infinity"]);
}
};
</script>
</body>
</html>
