I was working on a cross-browser event handling system. And I asked some developers to review my code. One of the developers said that my implementation is based on callbacks and not real events. What is the difference?
I have provided the source code of my implementation below for your convenience (and also as a gist). So far, I haven't found any problems with it. It works fine with all browsers that I tested with.
I'm sorry for the bad description of the problem, I am not familiar with that pure-event part.
var evento = (function (window) {
var win = window
, doc = win.document
, _handlers = {}
, addEvent
, removeEvent
, triggerEvent;
addEvent = (function () {
if (typeof doc.addEventListener === "function") {
return function (el, evt, fn) {
el.addEventListener(evt, fn, false);
_handlers[el] = _handlers[el] || {};
_handlers[el][evt] = _handlers[el][evt] || [];
_handlers[el][evt].push(fn);
};
} else if (typeof doc.attachEvent === "function") {
return function (el, evt, fn) {
el.attachEvent(evt, fn);
_handlers[el] = _handlers[el] || {};
_handlers[el][evt] = _handlers[el][evt] || [];
_handlers[el][evt].push(fn);
};
} else {
return function (el, evt, fn) {
el["on" + evt] = fn;
_handlers[el] = _handlers[el] || {};
_handlers[el][evt] = _handlers[el][evt] || [];
_handlers[el][evt].push(fn);
};
}
}());
// removeEvent
removeEvent = (function () {
if (typeof doc.removeEventListener === "function") {
return function (el, evt, fn) {
el.removeEventListener(evt, fn, false);
Helio.each(_handlers[el][evt], function (fun) {
if (fun === fn) {
_handlers[el] = _handlers[el] || {};
_handlers[el][evt] = _handlers[el][evt] || [];
_handlers[el][evt][_handlers[el][evt].indexOf(fun)] = undefined;
}
});
};
} else if (typeof doc.detachEvent === "function") {
return function (el, evt, fn) {
el.detachEvent(evt, fn);
Helio.each(_handlers[el][evt], function (fun) {
if (fun === fn) {
_handlers[el] = _handlers[el] || {};
_handlers[el][evt] = _handlers[el][evt] || [];
_handlers[el][evt][_handlers[el][evt].indexOf(fun)] = undefined;
}
});
};
} else {
return function (el, evt, fn) {
el["on" + evt] = undefined;
Helio.each(_handlers[el][evt], function (fun) {
if (fun === fn) {
_handlers[el] = _handlers[el] || {};
_handlers[el][evt] = _handlers[el][evt] || [];
_handlers[el][evt][_handlers[el][evt].indexOf(fun)] = undefined;
}
});
};
}
}());
// triggerEvent
triggerEvent = function (el, evt) {
_handlers[el] = _handlers[el] || {};
_handlers[el][evt] = _handlers[el][evt] || [];
for (var _i = 0, _l = _handlers[el][evt].length; _i < _l; _i += 1) {
_handlers[el][evt][_i]();
}
};
return {
add: addEvent,
remove: removeEvent,
trigger: triggerEvent,
_handlers: _handlers
};
}(this));
I could not care less about your system being callback, event or lambda-calculus based. For the bit you expose here, it seems rather well-written and promises to do a good job (although I'm curious about how you handled removeEvent()
;)).
However, I have a few remarks about your implementation:
I am amazed at the number of people accepting the practice of checking existence of properties each time they are about to call them. Noone will swap your IE for a FF in the middle of a function call (and anyone stupid enough to define a document.addEventListener
property other than an actual ECMA-5 replacement deserves to be flogged to death, if you ask me), so check which platform you're on at start and be done with it, like so:
if (doc.addEventListener) {
addEvent = // ...
freeEvent = // ...
}
else if (doc.attachEvent) {
addEvent = // ...
freeEvent = // ...
}
/* etc. */
In IE8-, for instance, the target of the event will not be available the same way as in ECMA-5 conventions.
If you want to offer a real cross-browser interface, you should provide an unified execution context to the event handlers. This could include a "cancel" function that would translate to something like:
cancel = function (e) { e.returnValue = false; }; // IE8-
cancel = function (e) { e.preventDefault(); }; // ECMA-5
you should also restore this
to the target object under IE8-, and unify the target
and event.target
semantics.
If you really want to be nice to programmers, you could also tackle a few oddities, like
and probably a few others.
The way I did it for my own purposes was to have a wrapper generated around the actual handler, that could take care of all the platform pecularities and establish a consistent execution context before calling the actual user code.
A last remark: except for the beauty of it, I am not quite sure it is still necessary to support Netscape4-style events. But that is a matter of faith, so...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With