摘要:所谓自定义事件,就是有别于有别于带有浏览器特定行为的事件(类似click, mouseover, submit, keydown等事件),事件名称可以随意定义,可以通过特定的方法进行添加,触发以及删除。使用jq触发自定义事件//绑定事件 $("li").bind('abc',function(){ alert(11
所谓自定义事件,就是有别于有别于带有浏览器特定行为的事件(类似click, mouseover, submit, keydown等事件),事件名称可以随意定义,可以通过特定的方法进行添加,触发以及删除。
使用jq触发自定义事件
//绑定事件
$("li").bind('abc',function(){
alert(111111);
});
//触发事件
$("li").trigger('abc');
//弹出 111111使用JS添加 触发 删除 自定义事件
JS自定义事件
循序渐进便于接收。慢慢来~~
先看个简单的事件添加的例子:
element.addEventListener("click", function() {
// 我是临时工
});这是个简单的为DOM元素分配事件处理函数的方法(IE 不支持),有别于:
element.onclick = function() {
// 我是临时工
};addEventListener()可以为元素分配多个处理函数(而非覆盖),因此,我们可以继续:
element.addEventListener("click", function() {
// 我是二代临时工
});然后,当element被click(点击)的时候,就会连续触发“临时工”和“二代临时工”函数。
抽象→具象→本质→数据层
你有没有觉得这种行为表现有点类似于往长枪里面塞子弹(add),(扣动扳手 – click)发射的时候按照塞进去的顺序依次出来。这种行为表现为我们实现自定义事件提供了思路:我们可以定义一个数组,当添加事件的时候,我们push进去这个事件处理函数;当我们执行的时候,从头遍历这个数组中的每个事件处理函数,并执行。
当多个事件以及对应数据处理函数添加后,我们最终会得到一个类似下面数据结构的对象:
_listener = {
"click": [func1, func2],
"custom": [func3],
"defined": [func4, func5, func6]
}因此,如果我们脱离DOM, 纯碎在数据层面自定义事件的话,我们只要以构建、遍历和删除_listener对象为目的即可。
函数式实现
还是那句话,循序渐进,我们先看看函数式的实现(只展示骨干代码):
var _listener = {};
var addEvent = function(type, fn) {
// 添加
};
var fireEvent = function(type) {
// 触发
};
var removeEvent = function(type, fn) {
// 删除
};上面的代码虽然显得比较初级,但是目的亦可实现。例如:
addEvent("alert", function() {
alert("弹出!");
});// 触发自定义alert事件fireEvent("alert");但是,函数式写法缺点显而易见,过多暴露在外的全局变量(全局变量是魔鬼),方法无级联等。这也是上面懒得显示完整代码的原因,略知即可。
字面量实现
众所周知,减少全局变量的方法之一就是使用全局变量(其他如闭包)。于是,我们稍作调整(代码较长,为限制篇幅,使用了滚动条,完整显示点击这里 – JS交互, RSS中无效果):
var Event = {
_listeners: {},
// 添加
addEvent: function(type, fn) {
if (typeof this._listeners[type] === "undefined") {//number,boolean,string,function,object,undefined
this._listeners[type] = [];
}
if (typeof fn === "function") {
this._listeners[type].push(fn); // {'alert'=>[func1]}
}
return this;
},
// 触发
fireEvent: function(type) {
var arrayEvent = this._listeners[type];
if (arrayEvent instanceof Array) {//用于判断一个变量是否某个对象的实例
for (var i=0, length=arrayEvent.length; i<length; i+=1) {
if (typeof arrayEvent[i] === "function") {
arrayEvent[i]({ type: type }); //执行函数
}
}
}
return this;
},
// 删除
removeEvent: function(type, fn) {
var arrayEvent = this._listeners[type];
if (typeof type === "string" && arrayEvent instanceof Array) {
if (typeof fn === "function") {
// 清除当前type类型事件下对应fn方法
for (var i=0, length=arrayEvent.length; i<length; i+=1){
if (arrayEvent[i] === fn){
this._listeners[type].splice(i, 1);//arrayObject.splice(index,howmany,item1,.....,itemX)
break;
}
}
} else {
// 如果仅仅参数type, 或参数fn邪魔外道,则所有type类型事件清除
delete this._listeners[type];
}
}
return this;
}
};
//添加事件
Event.addEvent("alert", function(e) {
console.log("弹出!");
console.log(e);
});
console.log(Event._listeners);
// 触发自定义alert事件
Event.fireEvent("alert");
// 移除自定义alert事件
Event.removeEvent("alert");
// 触发自定义alert事件
Event.fireEvent("alert");//此时无事件触发字面量实现虽然减少了全局变量,但是其属性方法等都是暴露而且都是唯一的,一旦某个关键属性(如_listeners)不小心在某事件处reset了下,则整个全局的自定义事件都会崩溃。因此,我们可以进一步改进,例如,使用原型链继承,让继承的属性(如_listeners)即使出问题也不会影响全局。
原型模式实现
代码如下(相比上面增加了addEvents, fireEvents, removeEvents多事件绑定、执行与删除方法,篇幅较长,增加滚动限高,点击这里完整展示 – JS交互, RSS中无效果)(一堆代码看得头大,建议直接跳过)
var EventTarget = function() {
this._listener = {};
};
EventTarget.prototype = {
constructor: this,
//单事件绑定
addEvent: function(type, fn) {
if (typeof type === "string" && typeof fn === "function") {
if (typeof this._listener[type] === "undefined") {
this._listener[type] = [fn];
} else {
this._listener[type].push(fn);
}
}
return this;
},
//一次绑定多个事件
addEvents: function(obj) {
obj = typeof obj === "object"? obj : {};
var type;
for (type in obj) {
if ( type && typeof obj[type] === "function") {
this.addEvent(type, obj[type]);
}
}
return this;
},
//单事件触发
fireEvent: function(type) {
if (type && this._listener[type]) {
var events = {
type: type,
target: this
};
for (var length = this._listener[type].length, start=0; start<length; start+=1) {
this._listener[type][start].call(this, events);
}
}
return this;
},
//一次触发多个事件
fireEvents: function(array) {
if (array instanceof Array) {
for (var i=0, length = array.length; i<length; i+=1) {
this.fireEvent(array[i]);
}
}
return this;
},
//单事件移除
removeEvent: function(type, key) {
var listeners = this._listener[type];
if (listeners instanceof Array) {
if (typeof key === "function") {
for (var i=0, length=listeners.length; i<length; i+=1){
if (listeners[i] === key){
listeners.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._listener[type];
}
}
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;
}
};
console.log(EventTarget);
var myEvents = new EventTarget();
console.log(myEvents);
myEvents.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 || !/input|pre/i.test(target.tagName)) {
myEvents.fireEvents(["once", "infinity"]);
}
};然后,需要实现自定义事件功能时候,先new构造下:
var myEvents = new EventTarget(); var yourEvents = new EventTarget();
这样,即使myEvents的事件容器_listener跛掉,也不会污染yourEvents中的自定义事件(_listener安然无恙)。