Jake Barnes wrote:[color=blue]
> Using javascript closures to create singletons to ensure the survival
> of a reference to an HTML block when removeChild() may remove the last
> reference to the block and thus destory the block is what I'm hoping
> to achieve.
>
> I've never before had to use Javascript closures, but now I do, so I'm
> making an effort to understand them. I've been giving this essay a
> re-read:
>
>
http://jibbering.com/faq/faq_notes/closures.html
>
> Let me state the problem I face and the solution as I
> understand it. I wanted to create an AJAX interface to[/color]
'AJAX interface' can mean all things to all men.
[color=blue]
> some online software. As an experiment, I built this page:
>
>
http://www.publicdomainsoftware.org/ajaxExperiment.htm[/color]
That is nice for you, and irrelevant to anyone reading news off-line.
[color=blue]
> Click anywhere to get the controls and then click
> "Add paragraph" to add some text.[/color]
No thanks, re-connecting to the Internet just to look through reams of
unrelated javascript buried in masses of bloated HTML to find a tiny
pertinent fragment has not proved a worthwhile activity in the past.
[color=blue]
> The communication box that appears is a bit like a
> modal dialog box ... . When this box is called again,
> I'll want to remove some of the HTML, and then add
> other blocks of HTML.
>
> Let's assume that all the HTML that I'll use starts off
> on the page.[/color]
Seems reasonable.
[color=blue]
> The user arrives at the page and clicks somewhere to
> bring up the controls, then clicks "Change Background
> color". The HTML block in the div with the id of
> "backgroundColorPicker" is now placed in the communication
> box. The user then picks a color and the background color
> changes.
>
> Let's suppose the user then clicks "Add paragraph". If I
> have a reference to the communication box and I have a
> reference to the "backgroundColorPicker" div, then I
> could do this:
>
> refToCommunicationBox.removeChild(refToBackgroundC olorPicker);
>
> But now, as I understand it, the div "backgroundColorPicker"
> is gone for good.[/color]
So why do that? Why not re-append/insert the block of HTML to wherever
you got it from in the first place?
[color=blue]
> It can not be brought back except by refreshing the page
> and losing all of one's work. For the div
> "backgroundColorPicker" to remain as a thing that my script
> can reference, then at least one reference to it must survive.
> The easiest way for me to achieve this would be, at the top of
> the page, to declare a reference to it in global space, like
> this:
>
> var refToBackgroundColorPicker =
> document.getElementById("refToBackgroundColorPicke r");[/color]
The odds of successfully retrieving a reference to a DOM node with -
getElementById - at the "top of the page" (prior to the parsing of the
HTML BODY) are low.
[color=blue]
> then I could create a function like this:
>
> function getRefToBackgroundColorPicker() {
> var newRefToBgColorPicker =
> refToBackgroundColorPicker.cloneNode();[/color]
^
Without a true boolean argument the - cloneNode - method only clones the
Node upon which it is called, rather than the DOM branch it contains.
The likelihood is that the DIV node alone does not define the required
GUI interface.
[color=blue]
> return newRefToBgColorPicker;
> }[/color]
If your dialog is model why would you need more than one instance of the
DOM branch that defines it (which is the implication of cloning it,
assuming you actually were cloning the whole branch)?
Incidentally, the group's FAQ references instruction on formatting
posted code in a way that is suitable for this context; preserving
indentation, avoiding automated line wrapping and so on. If you want
people to read your code you should present it in a form that is as
readable as possible.
[color=blue]
> Then I just have to use this function whenver I want
> a reference to the div "backgroundColorPicker".[/color]
It actually returns a clone of the DOM Node not a reference to the
original.
[color=blue]
> This is basically a workable solution. The only problem with
> it is that I need a global variable for every HTML block that
> I'm going to have on my page. If I hope to someday build
> sophisticated software, this might lead to a very large
> number of global variables.[/color]
Oh yes, any system based upon the extensive use of global variables will
have become chaotic and un-maintainable by the time you get to 4000-odd
lines of code. It is a trap for the amateur, and you know enough not to
even start off down that path.
[color=blue]
> Also, there is still the risk that I might
> accidentally use removeChild() on the global variable,[/color]
Ah, do I perceive a misconception about the relationship between the
DOM, and DOM methods, and javascript (from this, combined with the
unexpected cloning above)? The act of removing a Node from the DOM
with - removeChild - is not going to impact the value of a global
variable. If the global variable refers to the Node before it is removed
it will refer to it after it is removed from the DOM, and the existence
of the reference will prevent the garbage collection of the Node. Having
said that, DOMs don't seem to like Nodes to be hanging around in a freed
state for long. They are much happier if you move Nodes from place to
place in a DOM or from one document to a related document fragment and
back.
[color=blue]
> destroying the only reference to that block of HTML,[/color]
The DOM structure refers to the Node, and the global variable refers to
the Node. Using - removeChild - will remove the Node from the DOM only.
[color=blue]
> and thus removing it from the
> page for good.
>
> I realize, though, that there is another way to do this,[/color]
Probably half a dozen other ways.
[color=blue]
> and, I think, it involves Javascript closures.[/color]
For a couple of them, yes. Though you want to watch out for the circular
references including DOM Nodes problem on IE browsers as if you hide
references to DOM Nodes away in closures they may become difficult to
explicitly free later, if the need arises.
[color=blue]
> But I'm not sure how to quite do it.
> I'd like to create a reference that is pretty much
> indestructible.[/color]
Closures are the nearest you will get to that.
[color=blue]
> A singleton.[/color]
I am not user that is a useful label here.
[color=blue]
> But, with my limited understanding, it seems like somehow
> one must always end up with a global variable for the HTML
> to survive after removeChild() has been used.[/color]
As - removeChild - only acts upon references to a Node within the DOM
structure any references to the same node in javascript structures will
not be altered.
[color=blue]
> This seems like a bit of a hack. I suspect that my
> conceptual thinking here is fuzzy. Can anyone give me
> a clue about what I've got wrong?
>
>
> function storeReferencesToHTMLSafely() {
> var arrayOfReferencesToHtmlBlocks = new Object();
> var setAndGetReferenceToHtml = new Object();
>
> setAndGetReferenceToHtml.get = function (idOfHtmlBlock) {
> var htmlBlockRef = arrayOfReferencesToHtmlBlocks[idOfHtmlBlock];
> if (!htmlBlockRef || htmlBlockRef == "" ||
> htmlBlockRef == undefined)[/color]
The NOT (- ! -) operator in the expression - !htmlBlockRef -
type-converts its operand to boolean and then evaluates as true for
false and false for true. The empty string type-converts to boolean
false, so the expression - !htmlBlockRef - will be true when -
htmlBlockRef == "" - is true. So this expression is worthless in your
logical OR expression.
Similarly - undefined - type-converts to boolean false so the
expression - !htmlBlockRef - is also true when - htmlBlockRef ==
undefined - is true. Making the third expression in your logical OR
worthless, and so the entire logical OR is redundant (The first
expression will always be true whenever either of the other two is true)
and can be replaced with:-
if(!htmlBlockRef){
// if - htmlBlockRef - is either an empty string or undefined we
come in here anyway.
// we also come in here is - htmlBlockRef - is null and numeric zero
or NaN.
}
And references to the global - undefined - variable were one of the more
recent additions to the language (Netscape 4+, IE 5+ and error producing
in preceding implementations), and have proved the source of much
confusion, with - if(typeof x != undefined){ ... - being a commonly
recurring mistake while learning the language (it is always true even
if - x - is undeclared and/or undefined as typeof always evaluates as a
non-empty string).
[color=blue]
> {
> arrayOfReferencesToHtmlBlocks[idOfHtmlBlock] =
> document.getElementById(idOfHtmlBlock);
> var htmlBlockRef = arrayOfReferencesToHtmlBlocks[idOfHtmlBlock];[/color]
^^^
Multiple declarations of the same variable are pointless (though
harmless).
[color=blue]
> }
> var newHtmlBlockRef = htmlBlockRef.cloneNode(true);[/color]
^^^ ^^^^
Dito. ||||
////
Deep cloning this time, but why clone at all?
However, if you always clone the original DOM Node, and never call -
removeChild - or move on the original (it always stays at its original
location in the DOM) then why it is a problem to be throwing away
references to the clones, as the original can then be re-cloned? (It
would not be very efficient to do that (with the inefficient browser
garbage collection you would probably be looking at quite steep and
one-way increases in memory consumption)).
[color=blue]
> return newHtmlBlockRef;
> }
>
> return setAndGetReferenceToHtml;
> }
>
> then I could have this line in global space:
>
> var allHtmlReferencesObject = storeReferencesToHTMLSafely();
>
>
> But this is the wrong way to do things? Is there a
> cleaner approach?[/color]
There is a great deal here that needs questioning/clarifying,
particularly all the cloning. There probably is a better approach to the
bigger problem, but that may not necessarily remove this particular
issue.
[color=blue]
> I admit my conceptual grasp of closures is weak, and what
> I've done here seems like it could be done without closures.[/color]
If you look at the cited description of closures you will see that the
mechanism in javascript is based entirely around a structure of
inter-referring objects (the scope chains and the function object that
refer to them). Any language that has references to objects (or
pointers) can implement a parallel structure and do in another way what
can be done with closures, and this is true of javascript as well. The
advantage of using closures in a language that has closures is that the
mechanism is built-in, available and relatively cheep to employ (rather
than programming your own).
[color=blue]
> Somehow, I've failed to
> achieve my goal.
>
> Is there a way for arrayOfReferencesToHtmlBlocks to be
> a singleton and for no variable to be needed in global space?[/color]
You have already done that, so long as - storeReferencesToHTMLSafely -
is only called once. You can guarantee that the equivalent is only
called once by instead using the execution of a function expression to
create the closure:-
var safeStoreForHTMLReferences = (function(){
var refToHtmlBlocks = {};
return ({
get:function(id){
var htmlBlockRef;
if(
(
(htmlBlockRef = refToHtmlBlocks[id])||
(
(htmlBlockRef =
(refToHtmlBlocks[id] = document.getElementById(id))
)
)
)&&
(htmlBlockRef.cloneNode)
){
return htmlBlockRef.cloneNode(true);
}
return null;
}
});
})();
called as:-
var x = safeStoreForHTMLReferences.get('someId');
[color=blue]
> I suppose what I've done here does take me toward my goal.
> I'd never use removeChild() on allHtmlReferencesObject,[/color]
It would error if you did at that object does not implement the method,
and cannot be used as an argument for the method of objects that do.
[color=blue]
> so that would at least take me part way toward where I
> want to go - the risk of accidentally removing the last
> reference to a block of HTML would be greatly reduced.
>
> Any thoughts, feedback, comments?[/color]
Richard.