这篇文章发布于 2012年04月1日,星期日,18:06,归类于 JS实例。 阅读 366622 次, 今日 4 次 50 条评论
by zhangxinxu from http://www.zhangxinxu.com
本文地址: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; } } } else { // 如果仅仅参数type, 或参数fn邪魔外道,则所有type类型事件清除 delete this._listeners[type]; } } return this; } };
使用类似下面:
Event.addEvent("alert", function() { alert("弹出!"); }); // 触发自定义alert事件 Event.fireEvent("alert");
您可以狠狠地点击这里:JS自定义事件字面量书写demo
默认页面document
通过Event.addEvent()
绑定了两个自定义的alert
事件,因此,此时您点击页面的空白区域(非按钮与示例代码区域),就会有如下图所示的连续两个alert框:
demo页面还有两个按钮,用来清除已经绑定的alert
事件。第一个按钮清除所有alert
事件,而点击第二个按钮清除第一个alert
事件。例如我们点击第二个按钮:
清除完毕后再点击页面的空白区域, 您会发现只会弹出“第二个弹出!”字样的弹出框了。这表明,第一个绑定自定义事件被remove掉了。
字面量实现虽然减少了全局变量,但是其属性方法等都是暴露而且都是唯一的,一旦某个关键属性(如_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; } };
啰哩吧嗦的代码直接跳过,其实上面代码跟字面量方法相比,就是增加了下面点东西:
var EventTarget = function() {
this._listener = {};
};
EventTarget.prototype = {
constructor: this,
// .. 完全就是字面量模式实现脚本
};
然后,需要实现自定义事件功能时候,先new
构造下:
var myEvents = new EventTarget(); var yourEvents = new EventTarget();
这样,即使myEvents
的事件容器_listener
跛掉,也不会污染yourEvents
中的自定义事件(_listener
安然无恙)。
您可以狠狠地点击这里:原型模式下的JS自定义事件demo
从demo右半区域的源代码展示可以看出如何使用addEvents
, fireEvents
方法同时添加和触发多个自定义事件的。
//zxx: 下面为广告~~注意不要勿点~~嘻嘻~~
三、DOM自定义事件
我们平常所使用的事件基本都是与DOM元素相关的,例如点击按钮,文本输入等,这些为自带浏览器行为事件,而自定义事件与这些行为无关。例如:
element.addEventListener("alert", function() { alert("弹出!"); });
这里的alert
就属于自定义事件,后面的function
就是自定义事件函数。而这个自定义事件是直接绑定在名为element
的DOM元素上的,因此,这个称之为自定义DOM事件。
由于浏览器的差异,上面的addEventListener
在IE浏览器下混不来(attachEvent
代替),因此,为了便于规模使用,我们需要新的添加事件方法名(合并addEventListener
和attachEvent
),例如addEvent
, 并附带事件触发方法fireEvent
, 删除事件方法removeEvent
,(命名均参考自MooTools库)。
如何直接在DOM上扩展新的事件处理方法,以及执行自定义的事件呢?
如果不考虑IE6/7浏览器,我们可以直接在DOM上进行方法扩展。例如添加个addEvent
方法:
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); }); } };
//zxx: 上面代码中的HTMLElement
表示HTML元素。以一个<p>
标签元素举例,其向上寻找原型对象用过会是这样:HTMLParagraphElement.prototype
→ HTMLElement.prototype
→ Element.prototype
→ Node.prototype
→ Object.prototype
→ null
。这下您应该知道HTMLElement
所处的位置了吧,上述代码HTMLElement
直接换成Element
也是可以的,但是会让其他元素(例如文本元素)也扩展addEvent
方法,有些浪费了。
这样,我们就可以使用扩展的新方法给元素添加事件了,例如一个图片元素:
elImage.addEvent("click", function() { alert("我是点击图片之后的弹出!"); });
由于IE6, IE7浏览器的DOM水平较低,无法直接进行扩展,因此,原型扩展的方法在这两个浏览器下是行不通的。要想让这两个浏览器也支持addEvent
方法,只能是页面载入时候遍历所有DOM,然后每个都直接添加addEvent
方法了。
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); }); }; }
您可以狠狠地点击这里:基于DOM扩展自定义方法demo
点击demo页面张含韵小姐年轻时候相片,就会有该图片alt
属性值。
测试代码如下(demo页面有代码完整展示):
<img id="image" loading="lazy" src="https://image.zhangxinxu.com/image/study/s/s256/mm1.jpg" alt="年轻的张含韵" />
document.getElementById("image").addEvent("click", function() {
alert("这是:" + this.alt);
});
只能点到为止
直接在DOM上进行事件方法扩展其实是个糟糕的做法,因此,这里我并没有对自定义事件做进一步深入探讨(这个下一部分会讲)。
基于DOM扩展缺点有:缺少标准无规律、提高冲突可能性、性能以及浏览器支持。
扩展名字任意命,很有可能就会与未来DOM浏览器本身支持的方法相互冲突;扩展无规律,很有可能出现A和B同名不同功能的扩展而造成冲突;IE6-7浏览器下所有扩展都要通过遍历支持,其性能开销可想而知;另外IE8对DOM扩展的支持并不完整,例如其支持Element.prototype
,却没有HTMLElement.prototype
.
虽然我从事的站点就是基于MooTools库的,但是,我对MooTools库基于DOM扩展方法的做法是不支持的。相反,我更亲近jQuery库的做法,也就是下面要讲的“伪DOM自定义事件”。
四、伪DOM自定义事件
这里的“伪DOM自定义事件”是自己定义的一个名词,用来区分DOM自定义事件的。例如jQuery库,其是基于包装器(一个包含DOM元素的中间层)扩展事件的,既与DOM相关,又不直接是DOM,因此,称之为“伪DOM自定义事件”。
//zxx: 下面即将展示的代码目的在于学习与认识,要想实际应用可能还需要在细节上做些调整。例如,下面测试的包装器仅仅只是包裹DOM元素,并非选择器之类;$
符号未增加冲突处理,且几个重要方法都暴露在全局环境中,没有闭包保护等。
原型以及new
函数构造不是本文重点,因此,下面这个仅展示:
var $ = function(el) { return new _$(el); }; var _$ = function(el) { this.el = el; }; _$.prototype = { constructor: this, addEvent: function() { // ... }, fireEvent: function() { // ... }, removeEvent: function() { // ... } }
于是我们就可以使用类似$(dom).addEvent()
的语法为元素添加事件了(包括不包含浏览器行为的自定义事件)。
自定义事件的添加
如果只考虑事件添加,我们的工作其实很简单,根据支持情况,addEventListener
与attachEvent
方法分别添加事件(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; }
显然,事情不会这么简单,有句古话叫做“上山容易下山难”,自定义事件添加容易,但是如何触发它们呢?——考虑到自定义事件与浏览器行为无关,同时浏览器没有直接的触发事件的方法。
自定义事件的触发
又是不可避免的,由于浏览器兼容性问题,我们要分开说了,针对标准浏览器和IE6/7等考古浏览器。
1. 对于标准浏览器,其提供了可供元素触发的方法:element.dispatchEvent()
. 不过,在使用该方法之前,我们还需要做其他两件事,及创建和初始化。因此,总结说来就是:
document.createEvent() event.initEvent() element.dispatchEvent()
举个板栗:
$(dom).addEvent("alert", function() { alert("弹弹弹,弹走鱼尾纹~~"); }); // 创建 var evt = document.createEvent("HTMLEvents"); // 初始化 evt.initEvent("alert", false, false); // 触发, 即弹出文字 dom.dispatchEvent(evt);
createEvent()
方法返回新创建的Event
对象,支持一个参数,表示事件类型,具体见下表:
参数 | 事件接口 | 初始化方法 |
---|---|---|
HTMLEvents | HTMLEvent | initEvent() |
MouseEvents | MouseEvent | initMouseEvent() |
UIEvents | UIEvent | initUIEvent() |
关于createEvent()
方法我自己了解也不是很深入,不想滥竽充数,误人子弟,所以您有疑问我可能作答不了,希望对熟知该方法的人可以做进一步的解释说明(例如事件接口与document
关系,UIEvent
是什么东西等)。
initEvent()
方法用于初始化通过DocumentEvent
接口创建的Event
的值。支持三个参数:initEvent(eventName, canBubble, preventDefault)
. 分别表示事件名称,是否可以冒泡,是否阻止事件的默认操作。
dispatchEvent()
就是触发执行了,dom.dispatchEvent(eventObject)
, 参数eventObject
表示事件对象,是createEvent()
方法返回的创建的Event
对象。
2. 对于IE浏览器,由于向下很多版本的浏览器都不支持document.createEvent()
方法,因此我们需要另辟蹊径(据说IE有document.createEventObject()
和event.fireEvent()
方法,但是不支持自定义事件~~)。
IE浏览器有不少自给自足的东西,例如下面要说的这个"propertychange"
事件,顾名思意,就是属性改变即触发的事件。例如文本框value
值改变,或是元素id
改变,或是绑定的事件改变等等。
我们可以利用这个IE私有的东西实现自定义事件的触发,大家可以先花几分钟想想……
// zxx: 假设几分钟已经过去了……
大家现在有思路了没?其实说穿了很简单,当我们添加自定义事件的时候,顺便给元素添加一个自定义属性即可。例如,我们添加自定义名为"alert"
的自定义事件,顺便我们可以对元素做点小手脚:
dom.evtAlert = "2012-04-01";
再顺便把自定义事件fn
塞到"propertychange"
事件中:
dom.attachEvent("onpropertychange", function(e) { if (e.propertyName == "evtAlert") { fn.call(this); } });
这个,当我们需要触发自定义事件的时候,只要修改DOM上自定义的evtAlert
属性的值即可:
dom.evtAlert = Math.random(); // 值变成随机数
此时就会触发dom
上绑定的onpropertychange
事件,又因为修改的属性名正好是"evtAlert"
, 于是自定义的fn
就会被执行。这就是IE浏览器下事件触发实现的完整机制,应该说讲得还是蛮细的。
自定义事件的删除
与触发事件不同,事件删除,各个浏览器都提供了对于的时间删除方法,如removeEventListener
和detachEvent
。不过呢,对于IE浏览器,还要多删除一个事件,就是为了实现触发功能额外增加的onpropertychange
事件:
dom.detachEvent("onpropertychange", evt);
大综合
结合上面所有论述与展示,我们可以得到类似下面的完整代码(为限制篇幅,滚动定高,想查看完整代码推荐去原demo,或是点击这里完整显示— js交互,RSS中无效果。):
var $ = function(el) { return new _$(el); }; var _$ = function(el) { this.el = (el && el.nodeType == 1)? el: document; }; _$.prototype = { constructor: this, 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; } } else if (window.attachEvent) { el.attachEvent("on" + type, fn); if (isNaN(el["cu" + type])) { // 自定义属性 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); } } 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) { el.detachEvent("onpropertychange", arrEv[i]); } } } return this; } };
您可以狠狠地点击这里:JS DOM自定义事件demo
demo页面中的的张含韵小姐图片上通过级联形式联系添加了三个事件(一个是包含浏览器行为的click
事件,还有两个是自定义不含行为的alert
事件):
$(elImage) .addEvent("click", funClick); .addEvent("alert", funAlert1) .addEvent("alert", funAlert2);
而funClick
方法中有等同下面脚本:
$(e.target).fireEvent("alert");
因此,点击图片,才会出现三个弹出框:用户点击图片 → 执行funClick
→ 第一个弹框 → 执行fireEvent
→ 触发自定义"alert"
事件 → 连续两个"alert"
事件弹框
当点击图片下面的按钮清除掉自定义"alert"
事件后,再点击图片就只有一个弹出咯(funAlert1
和funAlert2
提前回家扫墓去了)!
五、清明节前的结语
明天回家,很显然,我要钓鱼钓死在河边上。
时间等客观原因,本文展示的些脚本并未做非常详尽严谨的测试,因此,不建议直接Copy到实际项目中应用,更多旨在相互交流与学习。例如在IE浏览器下,最后的“伪DOM自定义事件”,click事件通过点击触发时的事件类型(event.type
)是click
, 但是通过fireEvent
触发的时候事件类型是propertychange
, 这些细节在测试学习的时候都是可以忽略的,但是要是实际应用,这都是需要完善的。
本想以很通俗易懂的语言阐述我想表达的内容,但是,现在回过头看看,做得并不好,术语,啰嗦的话语还是显得多了点,这方面的功力还需要加强,或许是本身理解不透彻的缘故,无法驾驭自然无法语言通俗化。
虽说自己JS方面的学习比两年前要好多了(那个时候连addEventListener
和attachEvent
放在一起干嘛的都不清楚),但是心里清楚的很,JS还是很薄弱的,跟真正优秀的JS开发人员相比,要积累的还有很多。什么时候能够像看有色小说一样把《JavaScript语言精粹》一书读下来,恩,估计可以有脸得瑟得瑟了~~
本文涉及的一些知识点欢迎补充提点,有表述不准确的地方欢迎指正。
最后,祝大家清明节快乐!额?怎么这句话怪怪的——上坟一般快乐不起来吧~~那大家祝我清明回家钓鱼大丰收,大爆箱!!哈哈!!
本文为原创文章,转载请注明来自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com]
本文地址:http://www.zhangxinxu.com/wordpress/?p=2330
(本篇完)
- JS CustomEvent自定义事件传参小技巧 (0.718)
- 我对原型对象中this的一个懵懂错误认识 (0.209)
- 小tips: 点击页面出现富强、民主这类文字动画效果 (0.167)
- 见多识广,介绍Web开发中current开头的一些API属性 (0.167)
- js面向数据编程(DOP)一点分享 (0.151)
- jQuery诞生记-原理与机制 (0.148)
- 代码精简 - 常见JavaScript代码缩写举例 (0.132)
- Safari不支持build-in自定义元素的兼容处理 (0.125)
- 输入框value属性赋值触发js change事件的实现 (0.125)
- 翻译-高质量JavaScript代码书写基本要点 (0.090)
- HTML5 drag & drop 拖拽与拖放简介 (RANDOM - 0.009)
张老师 有个 疑问:Event() 、CustomEvent() 和 你的DOM自定义事件 有什么区别吗?
疑问:最后的一个大总结,用addEventListener绑定事件时,没有把事件加入到数组,而用attachEvent后就全部推到了数组呢
张 搞错了,不知到这段代码张自己测试过没有,this 应该指向构造函数EventTarget.
张老师,在大标题 “二、JS自定义事件” 下的小标题 “原型模式实现” 里的代码:
EventTarget.prototype = {
constructor:this,
….
}
为啥constructor要指向this呀?this不是window吗
请问一下,怎么实现在input框点击enter键时,执行的是tab键的功能
手动模拟。
张老师好,自定义事件有什么用啊,最终还不是要浏览器的事件去触发,那样子还不如直接当做一个函数写在click事件里面,我就想知道自定义事件一般在哪里用,谢谢老师了
个人觉得,基于状态执行的场景时可以使用。比如一个对象3种状态:creat,active,destroy. 当我实例化这个对象,在一些业务逻辑下需要去执行某个特定的状态时,比较适合使用自定义事件。 只是个人理解…
自定义事件不一定是要浏览器触发的,你也可以在如ajax请求后触发一个事件,跟使用回调函数类似
博主谦虚,写的很好,特别受益,祝每天开心,保重身体,多写好文。
广告多的也是醉人。。。
“原型模式下的JS自定义事件demo…”
demo中的右栏倒数第四行 ‘ …if (!target || !/input|pre/i.test(target.tagName)) {…’
其中的 !target 为什么要取反呢?
谢谢.
因为后面需要用到target.tagName,如果target为undifined的话就报错了,不会触发后面的事件,!target可以确保target为undifined时也可以触发后面的事件。
张老师,移除事件的时候,元素上的事件对象是不是没有移除!。
var ev = document.createEvent(“HTMLEvents”);
ev.initEvent(type, capture || false, false);
// 在元素上存储创建的事件,方便自定义触发
if (!el[“ev” + type]) {
el[“ev” + type] = ev;
}
看我的名字···· (之后 华丽的结束符 😉
好文
张知识
旭哥,,长枪里赛子弹,不是先进后出么。。
自定义事件,触发条件是什么啊。比如click事件,是每点击一下就会触发,那自定义事件,是如何实现,这样的,而且事件的执行都是异步的,自定义事件也必须得那样吧
我的理解是自定义事件想要触发,最终都要归结到DOM的原生浏览器支持的事件上来,只不过如果项目需要将几个事件结合用的话,就可以用自定义的方式封装一下,这样绑定事件的时候会简单一下,举个栗子吧:zepto.js中有tap这个事件,实际上就是用touchstart ,touchmove和touchend三个事件组合实现的,这样我们只需要在用的时候绑定一个tap事件就可以了,不需要每次都把这三个事件都写一遍(纯属于个人简介,如有问题请指正)
“众所周知,减少全局变量的方法之一就是使用全局变量”
很有道理
求详解
} else if (key instanceof Array) {
for (var lis=0, lenkey = key.length; liskey[lis] 应该是这样吧?
fireEvent: function(type,params) {
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
});这里不大懂是干嘛的*/
arrayEvent[i](params);//如果这样写传个参过来就更溜了,
}
}
}
return this;
},
将事件类型封装到一个对象中作为事件处理函数的第一个参数。就像addEventListener(type, fn, useCapture)会将事件信息封装到e中作为第一个fn的第一个参数
EventTarget.prototype = {
constructor: this,
…
}
中的this应该是EventTarget吧?不然调用实例的时候指向的是window
yes
可否说说,怎么在自定义事件中,使用自定义的参数
比如说现在事件中只有event参数,如果我想允许传入多个呢
原型模式实现自定义事件 发现一个错误:
removeEvent函数里面
if (listeners[i] === listener) 应该为 if (listeners[i] === key)
@stone 多谢提醒,已修正。
同样发现了, 在 原型模式下的JS自定义事件demo 里面的源码里面 listener 还没改成 key
虽然小弟的js也很薄弱,但是细细看了这篇文章两遍之后还是发现有一些质疑:
1.博主的“事件”与“事件监听器”两个概念混淆在一块了。比如写到removeEventListenter是删除自定义事件的方法,但是这个明显是用来移除事件监听函数的,跟删除自定义事件没什么关系吧~
2.对“自定义事件”这个概念有不同理解,核心是“自定义”,应该是说可以自己来描述一个事件的定义,比如定义一个tripleclick(三击),核心应该是描述清楚这个三击到底是什么含义,就是连续点击三次鼠标后dispatchEvent这个tripleclick
上述两点无根无据,个人理解:)
@after9 确实是,已修正,多谢提醒。
弱弱的说句, “createEvent()方法返回新创建的Event对象,支持一个参数,表示事件类型,具体见下表:” 下面的表格, iniEvent应该是initEvent吧…
额,还是dean edward实现的dom自定义事件比较巧妙
最近正好在学习js自定义事件,这篇文章很值得学习
如何给伪DOM对象添加一个自定义事件,当对象的一个属性值发生改变时触发此事件(非IE浏览器中)。
你有没有觉得这种行为表现有点类似于往长枪里面塞子弹(add),(扣动扳手 – click)发射的时候按照塞进去的顺序依次出来。
这个比喻太恰当了,赞
不是很恰当,因为貌似事件是按注入顺序执行的,枪弹出来是倒序的。
// 触发
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;
},
中 arrayEvent[i]({ type: type }); 是什么意思,传入{tpyp:type}的作用是什么? 属于js 中的什么语法,期待你的回复。
我在这里也想不懂,自定义函数执行顺便传入了个对象作参数,可能另有用处吧。我测试去掉这个,结果不影响基本功能操作哦。
很精典,学习啦。
强力插入,,,,
楼主写了这么一大堆,其实放上五个字就可以了———“观察者模式”
希望博主回访留个链接~ 你这里有很多我可以学习的东西喔~ 嘿嘿
谢谢~
element.onclick = function() {
// 我是临时工
};
这段得让DOM加载完后才能绑定元素,
放在底部就OK了。
ie为 attachEvent;
//!ie内核浏览器
element.addEventListener(“click”, function() {
// 我是临时工
});
//ie内核浏览器
element.attachEvent(“onclick”, function() {
// 我是临时工
});
嗯, 我是自己造了一个事件系统, 因为是用来处理自定义事件的, 绑定对象是数据, 所以完全脱离了 DOM. 已经写了 600 多行写到快吐了..
沙发. 这些天正在写事件系统. 快折腾死了.
好的
你都弱了。。。让我们这些低等码农 情以何堪