Написал свой Event для сайта.
Перепробовал кучу вариантов и остановился на идее библиотеки от Ильи Кантора, но с некоторыми отличиями.
Вот, собственно код:
var Event=(function(){
var allList=new Array;
var errors=new Array;
var handler=function(evt){
evt.errors=new Array;
if(!evt.fix){
evt.errors=new Array;
if(!evt)evt=event;
if(!evt.preventDefault)evt.preventDefault=function(){evt.returnValue=false;}
if(!evt.stopPropagation)evt.stopPropagation=function(){evt.cancelBubble=true;}
if(!evt.target)evt.target=(evt.srcElement.nodeType==1)?evt.srcElement:evt.srcElement.parentNode;
if(!evt.relatedTarget&&evt.fromElement)evt.relatedTarget=(evt.fromElement==evt.target)?evt.toElement:evt.fromElement;
if(evt.pageX==null&&evt.clientX!=null){
var html=document.documentElement,body=document.body;
evt.pageX=evt.clientX+(html&&html.scrollLeft||body&&body.scrollLeft||0)-(html.clientLeft||0);
evt.pageY=evt.clientY+(html&&html.scrollTop||body&&body.scrollTop||0)-(html.clientTop||0);
}
if(!evt.which&&evt.button)evt.which=(evt.button&1?1:(evt.button&2?3:(evt.button&4?2:0)));
evt.fix=true;
}
var ele=this.ele||this;
for(var i=0;i<allList.length;i++){
if(allList[i].ele==ele){
for(var s=0;allList[i]&&allList[i].list[evt.type]&&s<allList[i].list[evt.type].length;s++){
if(allList[i].list[evt.type][s]){
var objCall=allList[i].list[evt.type][s];
try{
if((objCall.handler||objCall).call(this,evt)===false){
evt.stopPropagation();
evt.preventDefault();
}
if(evt.stopCall===true)break;
}catch(e){
errors.push(e);
if(!objCall.onerror)break;
if(objCall.onerror.call(evt,e)===false)break;
}
}
}
if(Event.debug&&errors.length)Event.debug(errors);
delete errors;
return;
}
}
}
return{
Add:function(obj,type,call){
var ele=obj.ele||obj;
if(ele.setInterval&&(ele!=window&&!ele.frameElement))ele=window;
for(var i=0;i<allList.length;i++){
if(allList[i]&&allList[i].ele==ele){
if(!allList[i].list[type]){
allList[i].list[type]=[call];
if(ele.addEventListener)ele.addEventListener(type,allList[i].handler,false);
else if(ele.attachEvent)ele.attachEvent('on'+type,allList[i].handler);
return;
}
for(var c=0;c<allList[i].list[type].length;c++)if(allList[i].list[type][c]==call)return;
allList[i].list[type].push(call);
return;
}
}
var objListener={ele:ele,handler:function(evt){handler.call(obj,evt)}};
objListener.list=new Object;
objListener.list[type]=[call];
allList.push(objListener);
if(ele.addEventListener)ele.addEventListener(type,objListener.handler,false);
else if(ele.attachEvent)ele.attachEvent('on'+type,objListener.handler);
},
Del:function(ele,type,call){
for(var i=0;i<allList.length;i++){
if(allList[i].ele==ele){
if(type&&call){
for(var s=0;s<allList[i].list[type].length;s++){
var objCaller=allList[i].list[type][s];
if(objCaller&&(objCaller.handler||objCaller)==(call.handler||call)){
delete allList[i].list[type][s];
return;
}
}
}else if(type){
ele['on'+type]=null;
delete allList[i].list[type];
}else{
for(var type in allList[i].list){
if(ele.removeEventListener)ele.removeEventListener(type,allList[i].handler,false);
else if(ele.detachEvent)ele.detachEvent('on'+type,allList[i].handler);
}
delete allList[i];
}
return;
}
}
},
Init:function(ele,type){
if(document.createEvent&&ele.dispatchEvent){
var evt=document.createEvent('HTMLEvents');
evt.initEvent(type,true,true);
ele.dispatchEvent(evt);
}else ele.fireEvent('on'+type,event);
}
}
})();
Схематическая структура массива allList:
[{
ele: DOMelement,
handler: listener,
list: [fnc1,fnc3,fnc2,...]
},
...
]
Отличия от библиотеки Ильи:
* Все присоединённые обработчики хранятся в приватном массиве allList, не засоряя тем самым DOM-узлы.
* Добавлен метод Init для кроссбраузерной генерации события. Предпологается доработка.
* Возможность вызывать обработчик в контексте любого объекта.
Ну и так... по-мелочи
Вобщем, использовать так:
Event.Add(ele,type,fnc);
ele - DOM-узел, либо объект, имеющий свойство ele, которое возвращает DOM-элемент. Обработчик будет вызван в контексте этого объекта. Таким образом, можно передавать произвольные параметры в обработчик, что иногда бывает очень удобно, например:
Event.Add({ele:ele,data1:'value1',data2:'value2'},type,fnc);
type - тип события.
fnc - обработчик.
Удалять так
Event.Del(ele,type?,fnc?);
ele - DOM-узел, которому необходимо удалить обработчик.
type - тип события. Необязательно.
fnc - обработчик. Необязательно.
Если указан тип и обработчик - из указанного узла удалится только этот обработчик.
Если обработчик не указан - из элемента удалятся все обработчики указанного типа.
Если указан только элемент - удаляются все обработчики.
Так же реализованы "мини-фичи", упоминаемые в статье:
* Если в обработчике объекту event установить свойство stopCall - вызов опоследующих обработчиков прекратится.
* Возвращаемые оператором return значения попадут в свойство lastResult объекта event для следующего обработчика. Так же если будет передано false - это вызовет методы stopPropagation и preventDefault для события.
* Если в обработчике возникает ошибка, она будет занесена в массив errors объекта event.
От вас же я хотел бы услышать комменты по оптимизации, найденых ошибках, ну и предложения по расширению общего функционала.