本文地址:http://www.zhangxinxu.com/wordpress/?p=2330
?
一、说明、引言
我JS还是比较薄弱的,本文的内容属于边学边想边折腾的碎碎念,可能没什么条理,可能有表述不准确的地方,可能内容比较拗口生僻。如果您时间紧迫,或者JS造诣已深,至此您就可以点击右侧广告(木有?则RSS或盗版)然后撤了。
事件是个大课题,真要从断奶开始讲起的话,可以写个12期的连载。关于JS事件的文章(类似DOM/BOM事件模型,IE与其他浏览器事件差异,DOM1/DOM2事件定义等)落叶般随处可见。熟豆子反复炒一点意思都没有,因此,这里谈谈自己感兴趣的自定义事件以及周边。
所谓自定义事件,就是有别于有别于带有浏览器特定行为的事件(类似click
, mouseover
, submit
, keydown
等事件),事件名称可以随意定义,可以通过特定的方法进行添加,触发以及删除。
二、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") { this._listeners[type] = []; } if (typeof fn === "function") { this._listeners[type].push(fn); } 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); break;