Показать сообщение отдельно
  #218 (permalink)  
Старый 06.10.2009, 13:24
Аватар для B~Vladi
Модератор Всея Форума
Отправить личное сообщение для B~Vladi Посмотреть профиль Найти все сообщения от B~Vladi
 
Регистрация: 14.05.2009
Сообщений: 4,021

Написал свой 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.

От вас же я хотел бы услышать комменты по оптимизации, найденых ошибках, ну и предложения по расширению общего функционала.

Последний раз редактировалось B~Vladi, 06.10.2009 в 18:51.
Ответить с цитированием