Any need for addEventListener() and attachEvent() at all?

P

Peter Michaux

Hi,

Today I have been testing the event models from Netscape 4.8 and IE 4
to the current crop of browsers. I'd like to write a small event
library similar in purpose to the Yahoo! UI event library but with less
features and code. The Yahoo! event library is one of the best
libraries in YUI but it still seems to me to have some confused
code...that or I'm still confused.

The Yahoo! UI library focuses on using addEventListener and
attachEvent. However, due to the click and dblclick bugs in Safari a
long legacy event workaround is included to use a Netscape4-type event
model for Safari. Something like this

var listeners = [function(event){}, function(event){}];
document.getElementById('blue').onmouseover = function(event) {
for (var i=0; i<listeners.length; i++) {
listeners(event);
}
};

With this above example, multiple handler functions can be fired for a
single event. I imagine that this is an old trick that has been around
for a long time, yes?

With all the new browsers I tested with this legacy workaround, the
listener handlers can use event.stopPropogation() or
event.cancelBubble=true and they work as desired. The handler functions
can also use event.preventDefault() and event.returnValue=false and
they too work. These seem to work because the event object passed to
the handlers is a modern event object and not one from Netscape4.

My question is, if Safari needs this legacy workaround, and the legacy
workaround seems to work in all the browsers that have addEventListener
or attachEvent, then why bother with the addEventListener and
attachEvent functions at all? Why not just use the legacy way for all
browsers and all type of events.?

Thank you,
Peter
 
P

Peter Michaux

Hi David,

Thanks for the reply.

David said:
I know what you're saying. I'd say for most basic, simple
event-handling tasks, DOM Level 0 (element.onclick etc.) are perfectly
fine, and the hack you mention for adding multiple events is very
useful and for most purposes will suffice. However, as I understand
it, there are still some advantages to DOM Level 2:

1. Event capturing (optional): You can choose to pre-empt other
handlers that might be in line to listen to this event by using event
capturing.

The IE event model natively supports event bubbling. An event library
could be written to take control of all event capture and bubbling and
thereby give event caputure to IE. I think I can get by fine without
event capture so this advantage of DOM Level 2 doesn't sway me much
now. Of course if event handling is wrapped in a library then later
capture support could be added so that IE can do it too.
2. De-registering events: When writing widgets, I often have different
modules adding event handlers to the same elements, and using DOM Level
2, they don't have to know about each other. De-registering an single
listener is much easier using the Level 2 DOM.

I agree it is much easier. But given that any event library that will
support Safari will have to have legacy events, then the pain of
de-regestering individual handlers from an array of handlers will have
to be included.
I wrote an Events module a while back that I've been employing in
production sites for a while now, and it's been working pretty well for
me. One thing this module does is help ensure cleanup of registered
event handlers. Just put Events.clear() in your window.onload handler
and any events you registered through this will be cleaned up, fixing
the IE closure memory leak problem (at least for these handlers):

<snip code>

Thanks for posting the code example. It is always interesting to see
other people's code.
if (!Events.register[node]) Events.register[node] = [];

Does using node like this really create a unique string for a node? If
two nodes are the same, ie identical list elements in the same list
will this work?
// Browser-specific implementations
if (document.addEventListener) {
Events.add = function(node, type, fn, capt) {
capt = capt || false;
node.addEventListener(type, fn, capt);
this._addToRegister(node, type, fn, capt);
};

In some versions of Safari, this won't work for click events where you
call preventDefault(). This also won't allow you to attach dblclick
handlers in some Safari. These are the problems that made me start
thinking using only DOM0 might be the way to go.

} else if (document.attachEvent) {
Events.add = function(node, type, fn) {
node.attachEvent('on'+type, fn);
this._addToRegister(node, type, fn);
};
} else {
Events.add = function(node, type, fn) {
node['on'+type] = fn;
};
}

This doesn't allow multiple handlers to be assigned to an element with
the DOM0 fallback. This could lead to unexpected behavior as handlers
overwrite each other. This is one advantage of using the Yahoo! UI
library where you can attach multiple handlers using the DOM0 approach.
This also means they can workaround the Safari problems.

<snip of synthetic event code>

Do you find you create synthetic events often? I haven't used them at
all but can imagine automated testing could use them well.
I'm not too worried about older browsers in my own code.

Fair enough. I would really like to write a robust event library that
just works and doesn't suprise me some day down the road. It could be a
pipe dream :)

What I'm wondering is if the DOM0 implementations in the browsers have
any bugs like the DOM2/Safari bugs.

Thanks again,
Peter
 
P

Peter Michaux

David said:
Yeah, I guess that's why I included the DOM 0 fallback. (Which could
be robustified by implementing the Yahoo algorithm.) The idea is you
have a good enough library that wraps all the browser inconsistencies
of all time; you develop this library with unit tests, and test &
refine on all browsers, then you just use that library for all your
apps and stop worrying about browser inconsistencies for every app you
develop. I share your vision!

Here is my rough draft of the library. If anyone is interested please
take a look and let me know what you think.

http://peter.michaux.ca/temp/event.html

I need to add many more unit tests and docs but I think that's enough
for today.

I wonder if you couldn't just implement the functionality of the DOM
Level 2 events module in JS - it looks like most of it's possible.
(Though it most certainly wouldn't be as fast as native support, where
available.)

I don't know if the performance would be noticably different even in a
dragdrop situation. After any bugs are out of the library above then
performance tests can be made.

Peter
 
P

Peter Michaux

Hi David,

David said:
In the example test cases you have, there's not a
problem, but consider your code here:

el['on' + type] = function(e) {
e = e || window.event;
var listeners = arguments.callee.listeners;
for (var i=0, len=listeners.length; i<len; i++) {
var l = listeners;
if (l) {l.wrappedFn(e);}
}
};

Here is the relevant W3C recommendation
(http://www.w3.org/TR/DOM-Level-2-Events/events.html):

quote
Any exceptions thrown inside an EventListener will not stop propagation
of the event. It will continue processing any additional EventListener
in the described manner.
/quote


This is a good point but why exactly would one of my handlers throw an
error :)

I was thinking there must be some advantages of using the W3C functions
but then I keep think that if Safari is one of the target browser then
I must make it run just as well on Safari. This is a program to the
lowest common denominator approach.

I was concerned about your implementation's synchronous implementation;
however, it appears that the standard is not clear on whether multiple
events should be called synchronously or asynchronously. (I believe
the FF is asynchronous for multiple events, which would improve
performance considerably. Need test to determine whether this is the
case.)

JavaScript is single threaded. Doesn't that mean only one handler can
run at a time?

However, what your implementation is missing is fallback error
handling. If an error occurs in one handler it will prevent the rest
of the handlers from executing.

I imagine a try-catch around call to each handler would take care of
this.

-------------

The other option is to figure out how to detect the Safari browsers
that have the click and dblclick event problems and push them down the
degredation path as though they are ancient browsers. Then the path
would be clear to use the W3C functions for all other browsers with
addEventListener().

Thanks for the feedback.

Peter
 
P

Peter Michaux

David said:
quote
Any exceptions thrown inside an EventListener will not stop propagation
of the event. It will continue processing any additional EventListener
in the described manner.
/quote

I just verified that IE6, Safari 2, Firefox 1.5 and Opera 9 really do
this. When an error is thrown in one handler it seems like the other
handlers still run.
I was concerned about your implementation's synchronous implementation;
however, it appears that the standard is not clear on whether multiple
events should be called synchronously or asynchronously. (I believe
the FF is asynchronous for multiple events, which would improve
performance considerably. Need test to determine whether this is the
case.)

How could this be checked? I just tried a few big loops in one handler
to keep it running and see if the other handler ran at the same time.
That didn't give any good information.

When would a handler really need to run for such a long period of time?

Peter
 
P

Peter Michaux

Peter Michaux wrote:

The Yahoo! UI library focuses on using addEventListener and
attachEvent. However, due to the click and dblclick bugs in Safari a
long legacy event workaround is included to use a Netscape4-type event
model for Safari. Something like this

var listeners = [function(event){}, function(event){}];
document.getElementById('blue').onmouseover = function(event) {
for (var i=0; i<listeners.length; i++) {
listeners(event);
}
};


With all the new browsers I tested with this legacy workaround, the
listener handlers can use event.stopPropogation() or
event.cancelBubble=true and they work as desired. The handler functions
can also use event.preventDefault() and event.returnValue=false and
they too work. These seem to work because the event object passed to
the handlers is a modern event object and not one from Netscape4.

My question is, if Safari needs this legacy workaround, and the legacy
workaround seems to work in all the browsers that have addEventListener
or attachEvent, then why bother with the addEventListener and
attachEvent functions at all? Why not just use the legacy way for all
browsers and all type of events.?

For the record, I thought of an advantage to using addEventListener and
attachEvent.

If an element is served with an inline event handler like this

<div onmouseover="alert('hi');">

then using the legacy event workaround will clobber this inline
handler.

I've decided to have both the legacy workaround and the DOM2/IE types
like Yahoo! UI. I will use the legacy workaround for click and dblclick
events like Yahoo! UI does but I will do this for all browsers so they
are treated the same. For other event types I will use DOM2/IE.

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top