5

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)); 
10
  • I don't know what that developer is getting at. Did you ask? An event system can be any system that responds to a particular action, and causes some behavior when that action takes place. A callback is just a function that can be passed to another function to be invoked at a later time. Commented Oct 16, 2013 at 6:07
  • 1
    Note that in IE typeof doc.attachEvent returns object (where implemented) so those forks of your code will never be executed. Some host objects return unknown. Host objects are not required to conform to all details of ECMA-262, so you should not rely on them doing so. Commented Oct 16, 2013 at 6:10
  • What he said was "Nice code. Only problem? You are using callbacks and not events. :) dean.edwards.name/weblog/2009/03/callbacks-vs-events" Commented Oct 16, 2013 at 6:15
  • Thanks for noting, @RobG. So, just checking for doc.attachEvent as in else if (doc.attachEvent) { // } would be enough? Commented Oct 16, 2013 at 6:24
  • 1
    If that's your real question, then why did you ask what the difference between the two is? This guy's solution is given in the article. And just because that guy has decided that an error will prevent the rest of the handlers, that doesn't mean it isn't an event system. He's imposing an arbitrary distinction. Look at NodeJS. They have their own event system, and yet it behaves the same way. Commented Oct 16, 2013 at 6:39

1 Answer 1

2

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:

  • it is unnecessary to check on which browser you execute each time you add an event handler.

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. */ 
  • you offer an unified interface to attach a handler but, depending on the browser your code will execute on, the actual handler will behave differently.

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

  • the load event not firing in IE8- when an image is already cached
  • the absurdly complicated mouse wheel report system

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...

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.