addEvent - The late entry :)

A

Aaron Gray

I jokingly say this is the late entry :)

Okay I have read all the event entry comments from John's Resig's AddEvent
comepition blog :-

http://ejohn.org/projects/flexible-javascript-events/

and put together the following offering for my LGPL'ed library functions :-

function addEvent( el, type, fn, cascade) {
if ( el.addEventListener) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
}
else if ( el.attachEvent) {
el[type+fn] = function() {
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
else
el[ 'on'+type] = fn
}
function removeEvent( el, type, fn, cascade) {
if ( el.removeEventListener) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
else if ( el.detachEvent) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
else
el[ 'on'+type] = null
}


Lessons :-

call W3C first to satisfy Opera and for common sence, then MS as this is
usually easy detectable, then legacy.
Based on 'Weisi Su' entry on
http://ejohn.org/projects/flexible-javascript-events/#comment-276560 and
Michael White' suggestion using W3C first for correct operation on Opera.
plus legacy event handling added by me. Which in the end was all very
simular to some code I wrote the previous day and forgot about :)

Added cascade parameter that defaults to bubble on W3C calls.

The full test case can be found here :-

http://www.aarongray.org/Test/JavaScript/EventTest.html

Okay I have tried it on IE6 with and Drip and it does not seem to produce
and memory leaks AFAICS.

Drip IE memory leak detector :-

http://ejohn.org/projects/flexible-javascript-events/

I have tested it on 32Bit Vista IE7.0.6001, Safari 3.1.2, and Opera 9.51; XP
IE6.

Any browser testing appreciated, particularly on older and less well known
ones.

Well thats about it folks...any comments...holes...or suggesttions are most
welcome.

Regards,

Aaron
 
A

Aaron Gray

Aaron Gray said:
I jokingly say this is the late entry :)

Okay I have read all the event entry comments from John's Resig's AddEvent
comepition blog :-

http://ejohn.org/projects/flexible-javascript-events/

and put together the following offering for my LGPL'ed library functions
:-

function addEvent( el, type, fn, cascade) {
if ( el.addEventListener) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
}
else if ( el.attachEvent) {
el[type+fn] = function() {
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
else
el[ 'on'+type] = fn
}
function removeEvent( el, type, fn, cascade) {
if ( el.removeEventListener) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
else if ( el.detachEvent) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
else
el[ 'on'+type] = null
}


Lessons :-

call W3C first to satisfy Opera and for common sence, then MS as this is
usually easy detectable, then legacy.
Based on 'Weisi Su' entry on
http://ejohn.org/projects/flexible-javascript-events/#comment-276560 and
Michael White' suggestion using W3C first for correct operation on Opera.
plus legacy event handling added by me. Which in the end was all very
simular to some code I wrote the previous day and forgot about :)

Added cascade parameter that defaults to bubble on W3C calls.

The full test case can be found here :-

http://www.aarongray.org/Test/JavaScript/EventTest.html

Okay I have tried it on IE6 with and Drip and it does not seem to produce
and memory leaks AFAICS.

Drip IE memory leak detector :-

http://ejohn.org/projects/flexible-javascript-events/

I have tested it on 32Bit Vista IE7.0.6001, Safari 3.1.2, and Opera 9.51;
XP IE6.

Any browser testing appreciated, particularly on older and less well known
ones.

Well thats about it folks...any comments...holes...or suggesttions are
most welcome.

Here's some slightly more efficient code :-

if ( window.addEventListener) {
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
}
var removeEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
}
else if ( window.attachEvent) {
var addEvent = function( el, type, fn) {
el[type+fn] = function(){
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
var removeEvent = function( el, type, fn) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
}
else
{
var addEvent = function( el, type, fn, cascade) {
el[ 'on'+type] = fn
}
var removeEvent = function( el, type, fn) {
el[ 'on'+type] = null
}
}

Test case :-

http://www.aarongray.org/Test/JavaScript/EventTest2.html

Aaron
 
A

Aaron Gray

legacy case :-
{
var addEvent = function( el, type, fn, cascade) {

cascade parameter should not be here
el[ 'on'+type] = fn
}
var removeEvent = function( el, type, fn) {
el[ 'on'+type] = null
}
}

if ( window.addEventListener) {
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
}
var removeEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
}
else if ( window.attachEvent) {
var addEvent = function( el, type, fn) {
el[type+fn] = function(){
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
var removeEvent = function( el, type, fn) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
}
else
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = fn
}
var removeEvent = function( el, type, fn) {
el[ 'on'+type] = null
}
}

Correct test case :-

http://www.aarongray.org/Test/JavaScript/EventTest2.html

Aaron
 
A

Aaron Gray

Here's yet another mod :-

if ( window.addEventListener) {
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
}
var removeEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
}
else if ( window.attachEvent) {
var addEvent = function( el, type, fn) {
el[type+fn] = function(){
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
var removeEvent = function( el, type, fn) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
}
else
{

if ( isIE)
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = function(){
fn.call( el, window.event);
}
}
}
else
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = fn
}
}

var removeEvent = function( el, type, fn) {
el[ 'on'+type] = null
}
}

Needs 'isIE' variable setting.

Aaron
 
A

Aaron Gray

Aaron Gray said:
Here's yet another mod :-

if ( window.addEventListener) {
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
}
var removeEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
}
else if ( window.attachEvent) {
var addEvent = function( el, type, fn) {
el[type+fn] = function(){
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
var removeEvent = function( el, type, fn) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
}
else
{

if ( isIE)
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = function(){
fn.call( el, window.event);
}
}
}
else
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = fn
}
}

var removeEvent = function( el, type, fn) {
el[ 'on'+type] = null
}
}

Needs 'isIE' variable setting.

Here's a test case :-

http://www.aarongray.org/Test/JavaScript/EventTest3.html

Aaron
 
D

dhtml

Here's yet another mod :-

There are some significant differences in attachEvent and
addEventListener.

* w3c DOM Event bugs in Webkit and Safari (and probably a lot in Opera
that I'm not aware of).
* w3c DOM Events bubble as specified, IE events bubble differently,
like legacy events (by "legacy" events, I mean el.onclick = ...)
* different thisArg - attachEvent's this is always window

Needs 'isIE' variable setting.

Better stick to feature and capability detection.

Garrett
 
D

dhtml

Here's yet another mod :-

There are some significant differences in attachEvent and
addEventListener.

* w3c DOM Event bugs in Webkit and Safari (and probably a lot in Opera
that I'm not aware of).
* w3c DOM Events bubble as specified, IE events bubble differently,
like legacy events (by "legacy" events, I mean el.onclick = ...)
* different thisArg - attachEvent's this is always window

Needs 'isIE' variable setting.

Better stick to feature and capability detection.

Garrett
 
T

Thomas 'PointedEars' Lahn

dhtml said:
There are some significant differences in attachEvent and
addEventListener.

* w3c DOM Event bugs in Webkit and Safari (and probably a lot in Opera
that I'm not aware of).

Could you elaborate on that, please?
* w3c DOM Events bubble as specified, IE events bubble differently,
like legacy events (by "legacy" events, I mean el.onclick = ...)

Could you provide a test case that demonstrates the issue, please?
* different thisArg - attachEvent's this is always window

and

* event listeners *added* with addEventListener() are executed
*in order of addition*;

event listeners *attached* with attachEvent() are executed
*in arbitrary order*
Better stick to feature and capability detection.

Full ACK.


PointedEars
 
R

Richard Cornford

Aaron said:
I jokingly say this is the late entry :)

Okay I have read all the event entry comments from John's Resig's
AddEvent comepition blog :-

http://ejohn.org/projects/flexible-javascript-events/

and put together the following offering for my LGPL'ed library
functions :-

Even if you only count posts to this group, there have been so many
variations of this code posted over so many years that the odds are
extremely good that this code is already either someone else's copyright
or public domain. Its only unique feature is the inappropriate use of
"cascade" where most have used 'capturing'.
function addEvent( el, type, fn, cascade) {
if ( el.addEventListener) {
cascade = cascade || false;

Why do this here? You could parenthesise the logical OR expression and
use it as the final argument in the method call, saving the assignment
operation and one scope chain resolution of - cascade -, without making
the result significantly harder to understand.
el.addEventListener( type, fn, cascade)

But then why provide the - cascade - parameter at all as whatever you do
IE will not process events during a capturing phase because it does not
have one? And the intrinsic event property branch will not capture at
all without explicit calls to - captureEvents - methods (which are not
provided by all (or even most) browsers, and are not available on all
DOM nodes even when provided.

What you have done by providing the - cascade - parameter to the
function is invite programmes using the function to experience radically
different behaviour between browsers, to make themselves aware of those
differences, to test the environment themselves to see which behaviour
is going to apply and to do a great deal of coding to accommodate them.
If they are going to have to do all of that your function, as a method
for attaching the listeners, is almost more trouble than it is worth as
the actual attaching of the listeners is trivial in comparison.

The more sensible alternatives are to provide a full event capturing
emulation for the second and third branches (which had been demonstrated
possible but is a huge bloat for the function) or to specify this method
as 'at target' and 'bubbling' phase only, forget the cascade parameter,
and just use a false boolean literal as the final argument to this
method call.

It has been the norm in cross-browser scripting for event handling to be
'at target' and 'bubbling' phase only precisely because IE has never
provided a capturing phase for events, so most people's experience,
examples and literature will be geared towards that type of event
handling.
}
else if ( el.attachEvent) {
el[type+fn] = function() {

Type-converting the - fn - function into a string and using that as part
of the property name under which a reference to this function is stored
is a staggeringly stupid thing to do.

For a start ECMA 262 specifies the result of the native function -
toString - method as "An implementation dependent representation of the
function", so you have no guarantee about what it may contain at all.

Secondly, there have been bugs in implementation - toString - methods in
the past, which highlights the possibility of bugs in the future. A
specific example was to be found in Opera's javascript prior to version
7. The bug what that all regular expression literals were serialised
as - [object] - by its - toString - method. The result would be that two
functions that differed only in terms of a regular expression literal
would produce identical string representations.

But even if you assume that all function - toString - methods will
return a string representation that is as unique as the function's
source code, and that no bugs exist in the environments in question, the
uniqueness of such a string is nowhere near related to the uniqueness of
the identity of the function object. And it is the identify of the
function object that is important for the - attachEvent - and -
detachEvent - methods.

Consider what happens if the functions being attached represent one of
the many closure based methods of associating a method call with a
particular javascript object instance. A simple example might be:-

function associateObjectWithEvent(obj, methodName){
return (function(ev){
return obj[methodName](ev, this);
});
}

(but any of those clunky 'bind' methods so beloved of library authors
would do just as well as an example). You might very well want to attach
multiple examples of the function returned from that function to the
same event handler of a single element, but they will all serialize as
the same string despite having unique identity. Attach two and then
attempt to remove the first and it will be the second that is removed,
while the first can never then be removed (and that is on IE, the
browser with memory leak issue).
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
else
el[ 'on'+type] = fn

If this branch is ever good enough it is also always good enough.
Suggesting that the other two branches be removed entirely or that this
branch is not a suitable alternative to them. You invite as much
inconsistency with this as you did with your - cascade - parameter,
except this branch is the one that is least likely to be tested by the
programmer as modern browser will not tend to be entering this branch.
This means that the programmer may get the false impression that what
they are creating is cross-browser when in reality it is very likely to
behave unexpectedly in the event that the code encounters a browser that
will use this branch.

The - addEventListener - and - attachEvent - methods are all about
attaching multiple listeners to a single event on a single element. The
intrinsic event properties only allow the attaching of a single listener
to a single event on a single element. If you want an intrinsic event
property branch it should provide the full emulation of the other
methods and allow multiple factions to be attached. That can be done,
but again bloats the code, and once again brings into question the worth
of having the other two branches as once in place that single approach
would be as viable in all environments (and the absence of branching
would make the outcome much more consistent across browsers).

There is, of course, the question of cancelling default actions and
propagation. Browsers that only support intrinsic event properties may
be expecting default action cancelling to be achieved by returning false
from the function attached to the property, and may not allow the
cancelling of propagation at all. It takes quite a bit of work to
emulate the possibilities offered in the other branches for those
environments, and may mean imposing system-wide restrictions (like
forbidding propagation cancelling) in order to guarantee consistent
handling across browsers.
}
function removeEvent( el, type, fn, cascade) {
if ( el.removeEventListener) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
else if ( el.detachEvent) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
else
el[ 'on'+type] = null
}


Lessons :-

call W3C first to satisfy Opera and for common sence,
then MS as this is usually easy detectable, then legacy.
Based on 'Weisi Su' entry on
http://ejohn.org/projects/flexible-javascript-events/#comment-276560
and Michael White' suggestion using W3C first for correct
operation on Opera. plus legacy event handling added by me.
Which in the end was all very simular to some code I wrote
the previous day and forgot about :)

Added cascade parameter that defaults to bubble on W3C calls.

That was a mistake if you are not going to facilitate (emulate) a
capturing phase on browsers that don't use the W3C events DOM path.

Okay I have tried it on IE6 with and Drip and it does not seem
to produce and memory leaks AFAICS.
<snip>

Has drip been improved so that it can now detect closure based circular
references? It could not do that last time I looked at it. The
structures you create in the IE branch of this code will always produce
circular chains of references between DOM nodes and javascript objects,
and so you will only not provoke the issue if all listeners attached
with this method are later removed. Arranging that symmetrical handling
of listeners is another burden placed on the programmers using this
method. That is OK so long as they are told up-front that they will have
to address that issue themselves.

Well thats about it folks...any comments...holes...or suggesttions
are most welcome.

We will see. The biggest fault in your code is the inconsistent
interface it provides. Having the - cascade - parameter when it can only
influence the behaviour of some browsers is a bad idea. The fault of
trying to use the serialised function objects in the - addEvent - branch
is also significant. And the implication of the total, that the desired
outcome is a single general method, is also a mistake.

Recently I have been thinking about how to express what it is about the
attempt to be general that tends to results in code that bloated and
inefficient. Initially it occurred to me that being attempting to be
'general' in browser scripting has at least two dimensions; there are
the number of application contexts that are covered by the outcome, and
there are the number of browsers in which those application contexts are
accommodated.

These two dimensions can be confused so I should illustrate. In the
context here, and application context might be regarded as 'what you
intend doing', e.g.:-

1. You want to trigger some global code as the result of the user
activating (clicking or keyboard triggering) a button. You don't need to
know anything about the element that was activated, just what to do as a
result of its being activated.

2. You want the activation of a link to use methods of a series of
previously instantiated javascript objects to progressively create and
construct a query string that is added to the link's HREF before it is
used for navigation.

The first is very simple and does not require any preservation of -
this - references in attached handlers (as they are irrelevant) and does
not involve any 'binging' of javascript objects to functions. If it is
the most complex requirement of the system being built it can be
accommodated with extremely simple (and so by implication (in javascript
terms) fast) javascript code.

The second implies a need to reference the element activated and so
suggests that normalising the - this - references would be a good idea
(even if not essential (you could extract the element from the event
object's properties), it implies an ability to attach multiple listeners
to a single event and it requires a means of associating the listeners
with distinct javascript object instances.

Any method that accommodates the second will accommodate the first, but
at the cost of being bigger and slower (particularly if it is not going
to constrain the browser support dimension in the process). If a system
being created only needs the first to be accommodated then the code that
could accommodate the latter is over the top for that system.

You code cannot accommodate the latter because the need to bind
functions to javascript object instances would trip up the function
serialisation approach to function object identification. Thus you code
is more general that is needed for the first and not general enough for
the second, in the 'application contexts' dimension. The attempt to be
'general' usually ends up falling short of being truly general in that
dimension because the number of possible permutations can become so
great that they all cannot be accommodated behind a single API without
its becoming so ludicrously and obviously bloated that nobody would
consider using it. (As illustrated by the theoretical truly general
elopement position/dimension reporting method. Nobody has ever coded the
algorithm because the few people who appreciate what is involved realise
that executing 2000+ statements of code just to find out the page
relative x/y coordinates and width/height of an element is a
non-starter).

If the problem of attempting to be general is expressed as having two
dimension it is easy to why trying to extend the range of one of those
dimensions can have a disproportional impact on internal complexity,
bulk and performance. However, I have since realised that the attempt to
be 'general' has at least one more dimension.

There is a dimension of relevant knowledge, understanding and experience
(skills) of the people attempting to use any code that attempts to be
general. You will notice that one of arguments used to promote the
notion of general-purpose javascript libraries is that they allow
individual with relatively few relevant skills to achieve things that
would otherwise be beyond their abilities. And this is a direct
continuation of the copy-and-paste collections that have existed for
many years that (apparently, or superficially) allowed single
functionalities to be achieved by individuals through no more work than
pasting a block of code into an HTML document.

So where your code provides that - cascade - parameter, if properly
documented, a knowledgeable and experienced javascript programmer could
appreciate that if they wanted to use it the onus was on them to
accommodate the outcome. But to make the same API accommodate a wider
range possible 'programmers' that work would have to be moved inside
functions (so that the API's users did not have to face it). But that
would result in bloat, and it would result in a performance fall-off
(less so for W3C events DOM supporting browsers but pretty big for some
of the others).

With the ranges of at least three dimensions being expanded in the
pursuit of being 'general' the impact on internal complexity, bulk and
performance can be expected to be proportional to the cube of measure of
any signal dimension that is to be extended.

Richard.
 
R

Richard Cornford

Aaron said:
Here's yet another mod :-

if ( window.addEventListener) {

The W3C - EventTarget - interface is specified as being implemented by
objects implementing the core DOM Node interface (and in reality are
only reliable on objects implementing Element). The window object is not
a Node and so it is not valid to infer that if a window object has an -
EventTarget - method then all Nodes/Elements will, or vice versa. Opera
7, for example, had an - attachEvent - method on its window objects but
no - addEventListener -, while all of its nodes had - addEventListener -
methods. Thus you are forcing Opera 7 to use the (less efficient)
attachEvent branch having previously asserted that Opera browsers did
not work particularly well with that branch.
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
else
{

if ( isIE)
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = function(){
fn.call( el, window.event);

Are you suggesting here that IE 4 (which is below your ECMAScript 3
minimum for script support) did not call functions assigned to its
intrinsic event properties with the - this - value - being a referee to
the element to which the listener was attached? Or is this an attempt to
normalise the event object for the - fn - functions, and if so why not
do so in the next branch as non-IE browsers that are not DOM standard
may have chosen to only emulate IE in their event handling?
}
}
}
else
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = fn
}
}

Richard.
 
A

Aaron Gray

Richard Cornford said:
The W3C - EventTarget - interface is specified as being implemented by
objects implementing the core DOM Node interface (and in reality are only
reliable on objects implementing Element). The window object is not a Node
and so it is not valid to infer that if a window object has an -
EventTarget - method then all Nodes/Elements will, or vice versa. Opera 7,
for example, had an - attachEvent - method on its window objects but no -
addEventListener -, while all of its nodes had - addEventListener -
methods. Thus you are forcing Opera 7 to use the (less efficient)
attachEvent branch having previously asserted that Opera browsers did not
work particularly well with that branch.

Okay I have made a mod here then :-

if ( typeof addEventListener != "undefined")
...

This appears to function correctly on IE, FF, Safari, and Opera.

Thanks,

Aaron
 
A

Aaron Gray

Richard Cornford said:
Aaron said:
Here's yet another mod :-
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
else
{

if ( isIE)
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = function(){
fn.call( el, window.event);

Are you suggesting here that IE 4 (which is below your ECMAScript 3
minimum for script support) did not call functions assigned to its
intrinsic event properties with the - this - value - being a referee to
the element to which the listener was attached? Or is this an attempt to
normalise the event object for the - fn - functions, and if so why not do
so in the next branch as non-IE browsers that are not DOM standard may
have chosen to only emulate IE in their event handling?

So I can just drop that behaviour, and revert back to the previous version
for legacy support.

Aaron
 
P

Peter Michaux

I jokingly say this is the late entry :)

Okay I have read all the event entry comments from John's Resig's AddEvent
comepition blog :-

http://ejohn.org/projects/flexible-javascript-events/


I'd recommend not taking anyone's word as gospel in the JavaScript
world. There are certain programmers like Richard Cornford, Martin
Honnen and Douglas Crockford with track records for being correct that
I default to thinking they are correct and seriously wonder why if I
seem to think they are wrong. Otherwise I consider just about everyone
else wrong by default.

and put together the following offering for my LGPL'ed library functions :-

Why LGPL? There are JavaScript libraries under the MIT and BSD
licenses which are more liberal than LGPL.

Offering your library under the LGPL means, most importantly, you are
offering your library to the world. I think it is great you are taking
the project of writing a library seriously but if you are going to
release your library publicly you may want to give it a very low
version number like 0.0 or 0.1 so people know you are just starting to
learn the issues of both JavaScript and cross browser coding.

function addEvent( el, type, fn, cascade) {
if ( el.addEventListener) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
}
else if ( el.attachEvent) {
el[type+fn] = function() {
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
else
el[ 'on'+type] = fn}

Richard Cornford has pointed out some major problems in the code
above, the asymmetrical use of "cascade", the implicit toString of fn,
the non-equivalent fallback use of element properties like "onclick",
the creation of circular memory leaks in IE6 with no way to clean up.

What about the preventDefault problem in Safari versions something
like 2.0.2 and less? If an event listener for click or double click
events is added to addEventListener then preventDefault doesn't work?

What if the browser does support JavaScript but doesn't support any of
these types of event attachment APIs?

What if Internet Explorer 9 changes attachEvent to be an ActiveX
object so that the type converting test for el.attachEvent throws an
error even though a call to el.attachEvent() will work?

Why test which event model is the appropriate one for the browser each
time your addEvent is called when testing just the first time is
sufficient?

There are seemingly zillions of very fine details related to both
JavaScript and the browser environment that make writing your addEvent
function alone worthy of a very long investigation before declaring it
production ready for use on the general web and better than the
alternative libraries available for download today.

function removeEvent( el, type, fn, cascade) {
if ( el.removeEventListener) {
cascade = cascade || false;
el.removeEventListener( type, fn, cascade)
}
else if ( el.detachEvent) {
el.detachEvent( 'on'+type, el[type+fn])
el[type+fn] = null; // clear hash and IE memory leak
}
else
el[ 'on'+type] = null

}

Lessons :-

call W3C first to satisfy Opera and for common sence, then MS as this is
usually easy detectable, then legacy.

I think it would be a good idea to detect support for legacy before
using it. Better yet, follow Richard's advice that if legacy is good
enough it is always good enough or don't include legacy at all.
Based on 'Weisi Su' entry onhttp://ejohn.org/projects/flexible-javascript-events/#comment-276560and
Michael White' suggestion using W3C first for correct operation on Opera.
plus legacy event handling added by me. Which in the end was all very
simular to some code I wrote the previous day and forgot about :)

Added cascade parameter that defaults to bubble on W3C calls.

The full test case can be found here :-

http://www.aarongray.org/Test/JavaScript/EventTest.html

Okay I have tried it on IE6 with and Drip and it does not seem to produce
and memory leaks AFAICS.

Drip IE memory leak detector :-

http://ejohn.org/projects/flexible-javascript-events/

I have tested it on 32Bit Vista IE7.0.6001, Safari 3.1.2, and Opera 9.51; XP
IE6.

That is an extremely small set of OS/Browser combinations. I tested my
first try at a general purpose library on many

http://forkjavascript.org/welcome/browser_support

I plan on testing on quite a few more for v0.2 which I am writing now.

Any browser testing appreciated, particularly on older and less well known
ones.

Based on my experience, you will need to do all the testing yourself
which means having many operating systems available (e.g. Windows XP,
Mac OS X, Linux) and downloading many versions of old browsers. There
are many old versions of IE, O, S, NN, FF available on the web.

Well thats about it folks...any comments...holes...or suggesttions are most
welcome.

Specify the exact requirements for your event code. Post them to the
group for criticism.

Slow down. Read the YUI event library, as an example, until you think
you have found all the things that are wrong with it. Code a better
version. Read another event library and repeat the cycle. It is a long
road ahead.

Peter
 
R

Richard Cornford

Aaron said:
Richard said:
Aaron said:
Here's yet another mod :-
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
else
{

if ( isIE)
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = function(){
fn.call( el, window.event);

Are you suggesting here that IE 4 (which is below your ECMAScript
3 minimum for script support) did not call functions assigned to
its intrinsic event properties with the - this - value - being a
referee to the element to which the listener was attached? Or is
this an attempt to normalise the event object for the - fn -
functions, and if so why not do so in the next branch as non-IE
browsers that are not DOM standard may have chosen to only
emulate IE in their event handling?

So I can just drop that behaviour, and revert back to the previous
version for legacy support.

Again you miss the point. This is a question of providing a consistent
API. Either you normalise the event object and assert that the functions
passed in as listeners will receive a normalised event as their first
argument or you specify that the listeners are themselves responsible
for that aspect of event processing. It does not matter which you
choose, it is just that you should choose one or the other, document it
and implement it consistently.

Incidentally, if you are asked questions here you will get a lot
further, a lot faster, by answering them. Evasion is pointless.

Richard.
 
R

Richard Cornford

Aaron said:
Okay I have made a mod here then :-

if ( typeof addEventListener != "undefined")
...

This appears to function correctly on IE, FF, Safari, and Opera.

You have completely missed the point. The test was fine (well, the test
is actually debatable but I would have no problem with it), it is the
inference made from the test that is unfounded.

Richard.
 
P

Peter Michaux

On Jul 20, 1:37 am, Thomas 'PointedEars' Lahn <[email protected]>
wrote:

[snip]
* event listeners *added* with addEventListener() are executed
*in order of addition*;

The spec seems to disagree

"Although all EventListeners on the EventTarget are guaranteed to be
triggered by any event which is received by that EventTarget, no
specification is made as to the order in which they will receive the
event with regards to the other EventListeners on the EventTarget."

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-basic

[snip]
Full ACK.

"ACK"?

Emacs, Vim and various other text editors have autocompletion (a.k.a.
skeletons, snippets, etc) where you can type an abbreviation and the
editor expands the abbreviation to that for which it stands. Using
this sort of technology would add no extra burden to you when writing
a post but would make your posts much easier to read for those
unfamiliar with your uncommon English abbreviation. "ACK" does not
appear in my English dictionary, for example.

Peter
 
P

Peter Michaux

On Jul 20, 5:40 am, "Richard Cornford" <[email protected]>
wrote:

[snip]
(but any of those clunky 'bind' methods so beloved of library authors
would do just as well as an example).

Partial application is a common technique in lambda languages and not
looked down upon when used appropriately. The implementation in
JavaScript is not as aesthetic as in some other languages somewhat due
to the "this" issue in JavaScript; however, conceptually the binding
of some parameters to one functions and producing another function
taking no or less parameters is the same. I don't understand why you
would apparently balk at this concept. The use of a "bind" function is
not clunky, in my opinion.

[snip]
fn.call( el, window.event);
}
el.attachEvent( 'on'+type, el[type+fn])
}
else
el[ 'on'+type] = fn

If this branch is ever good enough it is also always good enough.

Functionally yes. I think, in this case, the third branch is
unnecessary even if implemented so all three branches have the same
behavior. If someone did think the third branch was necessary then the
first two branches (using addEventListener or attachEvent) could be
justified as performance boosts. A large portion of the remainder of
your message below is related to keeping code small which is really
just for a performance boost.
There is, of course, the question of cancelling default actions and
propagation.

Do you happen to know of a way to detect the Safari versions which do
not honour calls to preventDefault for click or double click events
when the listener was attached using addEventListener? There is a
"legacy" workaround using onclick and ondblclick properties of
elements but these are a bit ugly and have some drawbacks which need
documentation. At this point, since those versions of Safari have been
automatically upgraded, I'd rather put those versions of Safari down
the degradation path as though they didn't have event models at all. I
just don't know how to detect these versions of Safari.

[snip]
Recently I have been thinking about how to express what it is about the
attempt to be general that tends to results in code that bloated and
inefficient.

[snip interesting thoughts]

You an Matt Kruse have faced off many times about this whole "general"
library business. I don't quite see the fuss.

Matt is being a bit extreme by suggesting that the general position
reporting function should be written even though you have stated it
would be 2000+ statements and far too slow to be practical. Your
multiple implementations approach seems more appropriate in this
situation.

In messages like this one, you on the other hand seem to eschew things
"general" (though I don't think you do so 100%). Take, for example,
the scroll reporting code you wrote in the FAQ notes

http://www.jibbering.com/faq/faq_notes/not_browser_detect.html#bdScroll

I consider that level of "multi-browserness" sufficiently "general".
That is, I would be comfortable using this type of code on the
unrestricted web.

When you write that an "attempt to be general that tends to results in
code that bloated and inefficient" I think it is worth defining where
you draw the line between bloated and non-bloated code and efficient
and inefficient code.

If a "general" event library could be written in 10 lines would that
be acceptable to use in cases where it is more general than necessary?
What if it was 20 lines? 50 lines? 200 lines? 10000 lines? The
absolute size of the general code does matter to some extent.

Imagine the 200 line version's only drawback was download time and
initial interpretation, with no other runtime penalties. If that code
was already written, would it be worth using in situations where code
only 50% size could be used given the smaller code is not already
written. Writing 100 lines of event library code is probably not
trivial and require heavy testing. I would use the 200 line version as
it is ready, tested, cacheable, not really a download burden for the
majority of today's network connections (even most mobile networks).

I think that the extreme positions for and against "general" are both
faulty. When general code has acceptable performance, then creating
and maintaining one version is the winner. I think an event library
falls into this category. When the general code is unacceptably slow
then more optimized versions must be written. The position reporting
problem falls into this category.

"Premature optimization is the root of all evil" seems to apply and
suggests the strategy to use general code until there is push back
from some human (possibly the programmer, testers, customers) that the
code really is too slow. Then, and only then, fallback to the multiple
implementations strategy which is, in many regards, an optimization
strategy.

Peter
 
A

Aaron Gray

Richard Cornford said:
Aaron said:
Richard said:
Aaron Gray wrote:
Here's yet another mod :-
var addEvent = function( el, type, fn, cascade) {
cascade = cascade || false;
el.addEventListener( type, fn, cascade)
<snip>
else
{

if ( isIE)
{
var addEvent = function( el, type, fn) {
el[ 'on'+type] = function(){
fn.call( el, window.event);

Are you suggesting here that IE 4 (which is below your ECMAScript
3 minimum for script support) did not call functions assigned to
its intrinsic event properties with the - this - value - being a
referee to the element to which the listener was attached? Or is
this an attempt to normalise the event object for the - fn -
functions, and if so why not do so in the next branch as non-IE
browsers that are not DOM standard may have chosen to only
emulate IE in their event handling?

So I can just drop that behaviour, and revert back to the previous
version for legacy support.

Again you miss the point. This is a question of providing a consistent
API. Either you normalise the event object and assert that the functions
passed in as listeners will receive a normalised event as their first
argument or you specify that the listeners are themselves responsible for
that aspect of event processing. It does not matter which you choose, it
is just that you should choose one or the other, document it and implement
it consistently.

Incidentally, if you are asked questions here you will get a lot further,
a lot faster, by answering them. Evasion is pointless.

Sorry I find it hard to follow your arguments.

For the called event handler I was intending/trying to give a constsant
interface.

For the calling API I was trying to give support for setting single events
per element. But also hoping to offer extended functionality of individual
browsers. This may be a bad thing.

Aaron
 
A

Aaron Gray

Richard Cornford said:
You have completely missed the point. The test was fine (well, the test is
actually debatable but I would have no problem with it), it is the
inference made from the test that is unfounded.

So Opera needs a special case calling attachEvent on windows instead of
addEventListener ?

Aaron
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top