自己封装的javascript事件队列函数版
背景
javascript中使用addEventListener()或attachEvent()绑定事件时会有几个小问题:
一、使用addEventListener()或attachEvent()添加的匿名函数无法移除。
varoBtn=document.getElementById('btn'); oBtn.addEventListener('click',function(){ alert('buttonisclicked') },false) oBtn.reomveEventListener('click',function(){ alert('buttonisclicked') },false) //oBtn上的事件无法移除,因为传入的是一个匿名函数
二、ie6-ie8中,使用attachEvent()绑定多个事件的倒序执行问题。
varoBtn=document.getElementById('btn'); oBtn.attachEvent('onclick',function(){ alert(1) }) oBtn.attachEvent('onclick',function(){ alert(2) }) oBtn.attachEvent('onclick',function(){ alert(3) }) //ie9+ 下执行顺序1、2、3 //ie6-ie8下执行顺序3、2、1
解决问题
我想写一个跨浏览器的事件绑定模块,这样以后可以复用,同时我想解决上诉问题。JQuery内部使用事件队列和数据缓存机制解决此问题,看了下相关源码,实在复杂,自个试了一些方法,勉强实现。贴段代码,原来是用面向对象写的,不想让人看得很复杂,所有改成函数来组织。
/*绑定事件的接口 * *@param {dom-DOM}和{type-string}和{fn-function} 可选参数{fnName-string} *@execute 创建事件队列,添加到DOM对象属性上, 将事件处理程序(函数)加入事件队列 可为事件处理程序添加一个标识符,用于删除指定事件处理程序 */ functionbind(dom,type,fn,fnName){ dom.eventQueue=dom.eventQueue||{}; dom.eventQueue[type]=dom.eventQueue[type]||{}; dom.handler=dom.handler||{}; if(!fnName){ varindex=queueLength(dom,type); dom.eventQueue[type]['fnQueue'+index]=fn; } else{ dom.eventQueue[type][fnName]=fn; }; if(!dom.handler[type])bindEvent(dom,type); }; /*绑定事件 * *@param {dom-DOM}和{type-string} *@execute 只绑定一次事件,handler用于遍历执行事件队列中的事件处理程序(函数) *@caller bind() */ functionbindEvent(dom,type){ dom.handler[type]=function(){ for(varguidindom.eventQueue[type]){ dom.eventQueue[type][guid].call(dom); } }; if(window.addEventListener){ dom.addEventListener(type,dom.handler[type],false); } else{ dom.attachEvent('on'+type,dom.handler[type]); }; }; /*移除事件的接口 * *@param {dom-DOM}和{type-string}可选参数{fnName-function} *@execute 如果没有标识符,则执行unBindEvent() 如果有标识符,则删除指定事件处理程序,如果事件队列长度为0,执行unBindEvent() */ functionunBind(dom,type,fnName){ varhasQueue=dom.eventQueue&&dom.eventQueue[type]; if(!hasQueue)return; if(!fnName){ unBindEvent(dom,type) } else{ deletedom.eventQueue[type][fnName]; if(queueLength(dom,type)==0)unBindEvent(dom,type); }; }; /*移除事件 * *@param {dom-DOM}和{type-string} *@execute 移除绑定的事件处理程序handler,并清空事件队列 *@caller unBind() */ functionunBindEvent(dom,type){ if(window.removeEventListener){ dom.removeEventListener(type,dom.handler[type]) } else{ dom.detachEvent(type,dom.handler[type]) } deletedom.eventQueue[type]; }; /*判断事件队列长度 * *@param {dom-DOM}和{type-string} *@caller bind()unBind() */ functionqueueLength(dom,type){ varindex=0; for(varlengthindom.eventQueue[type]){ index++; } returnindex; };
使用方法
varoBtn=document.getElementById('btn'); //绑定事件 //为button同时绑定三个click事件函数 //ie6-ie8下执行顺序不变 bind(oBtn,'click',function(){ alert(1); }) bind(oBtn,'click',function(){ alert(2); },'myFn') bind(oBtn,'click',function(){ alert(3); }) //移除事件 //移除所有绑定的click事件函数,支持移除匿名函数 unBind(oBtn,'click') //只移除标识符为myfn的事件函数 unBind(oBtn,'click','myFn')
程序思路
程序主要思路就像将事件队列作为dom元素对象的一个属性,添加在dom元素上,而不会污染全局环境,这样可以解决不同dom元素绑定不同事件类型的多个事件函数的数据存储问题。
//dom元素上的事件队列 dom{ eventQueue:{ 'click':{ fnQueue1:function, myfn :function, fnQueue3:function } 'mouseover':{ fnQueue1:function, fnQueue2:function } } }
每次先把事件函数添加到对应事件类型的事件队列中,只绑定一次事件。触发事件时执行handler事件函数,handler则遍历执行事件队列中的事件函数。
unBind()如果没有传入标识符,则移除所有绑定的事件函数,支持移除匿名函数,如果有标识符则移除指定的事件函数。
程序逻辑并不复杂,可能有bug和性能问题,有兴趣可以指导交流下。