> Sure seems like something javascript should deal with.  Closures are
> tough, but perhaps some kind of "weak reference" support could solve
> that.  If an element is destroyed sure seems like javascript should
> figure out how to free up memory.

> Do these memory leaks persist after a full page reload?  The horrors.

Even a full page reload COULD persist these leaks, yes. But prototype
does handle that by wiring up an "onunload" event handler that purges
all remaining DOM handlers. Our problem comes into play because we are
starting to make applications where a full page reload may not occur for
an arbitrarily long period of time...

> Thanks very much for this.  I suspect it will take me a few passes to
> understand it.  Do you happen to have a small working example?

I don't have anything available example-wise yet that's in the public
domain... But myself, Marco Jaeger (whose Dialogs you may have seen
talked about here recently), Jerod Venema (who also helped Marco and I
with the Dialogs), and Joseph Potenza (all members of this list) are
currently working on something that should bring this pattern to light
in a big way (although progress is somewhat slow due to all of our life
obligations at the moment).

> By scanning the DOM looking for the prefix on the ids?

Exactly. How you achieve that (uniquely IDing your DOM elements) from
your server code I guess will depend on what platform you use... .NET
handles it all for me, but you may have to figure it out for yourself if
you use RoR or PHP. But getting this part right means you can easily
load in an N number of widgets of the same type, knowing full well there
will be no collisions of DOM element IDs when you try to wire up all the
events.

> bindAsEventListener() means that when the event fires you get the
> *instance* of the object in "this", correct?

Yes, that's exactly what it's for. bind() and bindAsEventListener() are
both for that purpose, but the latter also gives you access to the
"event" object.

> Is there any worry that bindAsEventListener(), as a closure, will also
> leak?

No, closures in and of themselves are fine. The problem comes in when
you have a reference to a DOM element within your closure, creating a
circular reference (mainly an IE problem, but again if you think about
this in a general manner you'll be safe on all browsers, as FF has it's
leaks as well). Check out the following link for an in depth
explanation:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechC
ol/dnwebgen/ie_leak_patterns.asp

> Here's where I get lost.  The event fires and then I free up the
> event bound to that element.  Where's the list of other events to
> free up before starting the ajax call?

This is why you should always keep handles to your event handlers, and
making autonomous widgets is so important (each widget knows which
events it has tied to which elements, and therefore knows how to clean
itself up). I always wire up all my DOM event handlers in 1 method
(called _attachEvents() in my case), and right before making the call to
the server I call my _detachEvents() method. When I say that, I mean in
each widget... each class has it's own _attach/_detachEvents()
methods... In _attachEvents, I know explicitly which elements I need to
attach to which event handlers, and I store all the handlers in member
variables of the widget... thus in the _detachEvents() method cleanup is
a snap. Like so...

//NOTE: This is obviously a pseudo-class just meant to illustrate the
attaching and detaching of event handlers... also notice the difference
in bind() and bindAsEventListener(), and the fact that I define a
dispose() method, which is where _detachEvents() is called from (this
way you can put other disposal related code in the same place -- like
un-registering any Draggables that the widget might have registered
using the DOM elements that are about to be annihilated)

MyWidget = Class.create()
MyWidget.prototype =
{
        initialize: function(id)
        {
                this.id = id;
                this._attachEvents();
        },

        _attachEvents: function()
        {
                //someElement1
                this.someElement1 = $(this.id + "_someElement1");
                this.someElement1_ClickListener =
this.someElement1_Clicked.bind();
                Event.observe(this.someElement1, "click",
this.someElement1_ClickListener);

                //someElement2
                this.someElement2 = $(this.id + "_someElement2");
                this.someElement2_ClickListener =
this.someElement2_Clicked.bindAsEventListener();
                Event.observe(this.someElement2, "click",
this.someElement2_ClickListener);
        },

        _detachEvents: function()
        {
                //someElement1
                Event.stopObserving(this.someElement1, "click",
this.someElement1_ClickListener);
                this.someElement1 = null;
                this.someElement1_ClickListener = null;

                //someElement2
                Event. stopObserving(this.someElement2, "click",
this.someElement2_ClickListener);
                this.someElement2 = null;
                this.someElement2_ClickListener = null;
        },

        someElement1_Clicked: function()
        {
                alert(this.id + "_someElement1 was clicked");
        },

        someElement2_Clicked: function(evt)
        {
                //notice the evt arg in this function... I have access
the "event" object if I need it because I used bindAsEventListener...
                alert(this.id + "_someElement2 was clicked");
        },

        dispose: function()
        {
                this._detachEvents();

                //insert other disposal code here (like unregistering
Draggables.. etc..)
        }
};

The information transmitted in this electronic mail is intended only for the
person or entity to which it is addressed and may contain confidential,
proprietary, and/or privileged material.  Any review, retransmission, 
dissemination or other use of, or taking of any action in reliance upon,
this information by persons or entities other than the intended recipient
is prohibited. If you received this in error, please contact the sender and
delete the material from all computers.

_______________________________________________
Rails-spinoffs mailing list
Rails-spinoffs@lists.rubyonrails.org
http://lists.rubyonrails.org/mailman/listinfo/rails-spinoffs

Reply via email to