addEvent - The late entry :)

Discussion in 'Javascript' started by Aaron Gray, Jul 16, 2008.

  1. Aaron Gray

    Aaron Gray Guest

    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
     
    Aaron Gray, Jul 16, 2008
    #1
    1. Advertising

  2. Aaron Gray

    Aaron Gray Guest

    Aaron Gray, Jul 16, 2008
    #2
    1. Advertising

  3. Aaron Gray

    Aaron Gray Guest

    "Aaron Gray" <> wrote in message
    news:...
    >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
     
    Aaron Gray, Jul 20, 2008
    #3
  4. Aaron Gray

    Aaron Gray Guest

    "Aaron Gray" <> wrote in message
    news:...

    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
     
    Aaron Gray, Jul 20, 2008
    #4
  5. Aaron Gray

    Aaron Gray Guest

    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
     
    Aaron Gray, Jul 20, 2008
    #5
  6. Aaron Gray

    Aaron Gray Guest

    "Aaron Gray" <> wrote in message
    news:...
    > 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
     
    Aaron Gray, Jul 20, 2008
    #6
  7. Aaron Gray

    dhtml Guest

    On Jul 19, 6:49 pm, "Aaron Gray" <> wrote:
    > 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

    >
    > Aaron
     
    dhtml, Jul 20, 2008
    #7
  8. Aaron Gray

    dhtml Guest

    On Jul 19, 6:49 pm, "Aaron Gray" <> wrote:
    > 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

    >
    > Aaron
     
    dhtml, Jul 20, 2008
    #8
  9. dhtml wrote:
    > On Jul 19, 6:49 pm, "Aaron Gray" <> wrote:
    >> 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).


    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*

    >> Needs 'isIE' variable setting.

    >
    > Better stick to feature and capability detection.


    Full ACK.


    PointedEars
    --
    realism: HTML 4.01 Strict
    evangelism: XHTML 1.0 Strict
    madness: XHTML 1.1 as application/xhtml+xml
    -- Bjoern Hoehrmann
     
    Thomas 'PointedEars' Lahn, Jul 20, 2008
    #9
  10. Aaron Gray wrote:
    >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.

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

    <snip>
    > 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.
     
    Richard Cornford, Jul 20, 2008
    #10
  11. Aaron Gray wrote:
    > 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)

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

    > }
    > }
    > }
    > else
    > {
    > var addEvent = function( el, type, fn) {
    > el[ 'on'+type] = fn
    > }
    > }


    Richard.
     
    Richard Cornford, Jul 20, 2008
    #11
  12. Aaron Gray

    Aaron Gray Guest

    "Richard Cornford" <> wrote in message
    news:g5vcod$65e$1$...
    > Aaron Gray wrote:
    >> 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.


    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
     
    Aaron Gray, Jul 20, 2008
    #12
  13. Aaron Gray

    Aaron Gray Guest

    "Richard Cornford" <> wrote in message
    news:g5vcod$65e$1$...
    > 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.

    Aaron
     
    Aaron Gray, Jul 20, 2008
    #13
  14. On Jul 16, 8:17 am, "Aaron Gray" <> wrote:
    > 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
     
    Peter Michaux, Jul 20, 2008
    #14
  15. Aaron Gray wrote:
    > Richard Cornford wrote:
    >> 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.

    Richard.
     
    Richard Cornford, Jul 20, 2008
    #15
  16. Aaron Gray wrote:
    > Richard Cornford wrote:
    >> Aaron Gray wrote:
    >>> 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.

    >
    > 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.
     
    Richard Cornford, Jul 20, 2008
    #16
  17. On Jul 20, 1:37 am, Thomas 'PointedEars' Lahn <>
    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
     
    Peter Michaux, Jul 20, 2008
    #17
  18. On Jul 20, 5:40 am, "Richard Cornford" <>
    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
     
    Peter Michaux, Jul 20, 2008
    #18
  19. Aaron Gray

    Aaron Gray Guest

    "Richard Cornford" <> wrote in message
    news:g5vtth$nsp$1$...
    > Aaron Gray wrote:
    >> Richard Cornford wrote:
    >>> 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
     
    Aaron Gray, Jul 20, 2008
    #19
  20. Aaron Gray

    Aaron Gray Guest

    "Richard Cornford" <> wrote in message
    news:g5vttk$nss$1$...
    > Aaron Gray wrote:
    >> Richard Cornford wrote:
    >>> Aaron Gray wrote:
    >>>> 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.

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


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

    Aaron
     
    Aaron Gray, Jul 20, 2008
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. AtomicBob
    Replies:
    14
    Views:
    911
    Toby Inkster
    May 2, 2006
  2. kaeli

    addEventHandler / addEvent conflict

    kaeli, Jan 14, 2004, in forum: Javascript
    Replies:
    4
    Views:
    202
    kaeli
    Jan 14, 2004
  3. simon
    Replies:
    4
    Views:
    244
    Dr J R Stockton
    Dec 15, 2006
  4. Replies:
    3
    Views:
    198
  5. Richard Maher

    IE leaks, circular references, addevent et al

    Richard Maher, Sep 15, 2009, in forum: Javascript
    Replies:
    30
    Views:
    465
    David Mark
    Sep 29, 2009
Loading...

Share This Page