using javascript closures to create singletons to ensure the survival of a reference to an HTML bloc

Discussion in 'Javascript' started by Jake Barnes, Feb 11, 2006.

  1. Jake Barnes

    Jake Barnes Guest

    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 some online software. As an
    experiment, I built this page:

    http://www.publicdomainsoftware.org/ajaxExperiment.htm

    Click anywhere to get the controls and then click "Add paragraph" to
    add some text. The communication box that appears is a bit like a modal
    dialog box (except I don't yet freeze the use of everything else, so it
    isn't really modal). I want to add blocks of HTML to this communication
    box so that the user can add an appropriate input. 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.
    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(refToBackgroundColorPicker);

    But now, as I understand it, the div "backgroundColorPicker" is gone
    for good. 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("refToBackgroundColorPicker");

    then I could create a function like this:

    function getRefToBackgroundColorPicker() {
    var newRefToBgColorPicker = refToBackgroundColorPicker.cloneNode();
    return newRefToBgColorPicker;
    }


    Then I just have to use this function whenver I want a reference to the
    div "backgroundColorPicker". 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. Also, there is still the risk that I might
    accidentally use removeChild() on the global variable, destroying the
    only reference to that block of HTML, and thus removing it from the
    page for good.

    I realize, though, that there is another way to do this, and, I think,
    it involves Javascript closures. But I'm not sure how to quite do it.
    I'd like to create a reference that is pretty much indestructible. A
    singleton. 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. 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)
    {
    arrayOfReferencesToHtmlBlocks[idOfHtmlBlock] =
    document.getElementById(idOfHtmlBlock);
    var htmlBlockRef = arrayOfReferencesToHtmlBlocks[idOfHtmlBlock];
    }
    var newHtmlBlockRef = htmlBlockRef.cloneNode(true);
    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? I
    admit my conceptual grasp of closures is weak, and what I've done here
    seems like it could be done without closures. 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?

    I suppose what I've done here does take me toward my goal. I'd never
    use removeChild() on allHtmlReferencesObject, 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?
     
    Jake Barnes, Feb 11, 2006
    #1
    1. Advertising

  2. Jake Barnes

    Jambalaya Guest

    Jake Barnes wrote:
    > 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.


    Then you have been misinformed. removeChild does not destroy the node.
    In fact, it return a reference to it.

    > I've never before had to use Javascript closures, but now I do,


    No you don't. Not for this scenario.
    [snipped remainder of perceived problems]

    > But this is the wrong way to do things? Is there a cleaner approach?


    Yes and yes. One possible solution would be to have one global variable
    where you store your references to your various nodes.

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
    <title></title>
    <style type="text/css">
    *{padding:0;margin:0;}
    html,body{height:100%;}
    body{font-size:100.01%;}
    ..tool{border:1px solid gray;margin:10px;padding:10px;background:#eee;}
    </style>
    <script type="text/javascript">
    function $(id){return document.getElementById(id)}

    var registry = {
    toggle: function(id){
    if (id in registry){
    registry[id].visible = !registry[id].visible;
    if (registry[id].visible){
    $('container').appendChild(registry[id]);
    } else {
    $('container').removeChild(registry[id]);
    }
    } else {
    registry[id] = $('container').removeChild($(id));
    registry[id].visible = false;
    }
    return false;
    }
    }
    </script>
    </head>
    <body>
    <div id="container">
    <div id="toolbox">
    <a href="jsreq.htm" onclick="return registry.toggle('tool1')">
    Tool 1</a>
    <a href="jsreq.htm" onclick="return registry.toggle('tool2')">
    Tool 2</a>
    <a href="jsreq.htm" onclick="return registry.toggle('tool3')">
    Tool 3</a>
    </div>
    <div id="tool1" class="tool">This is tool one.</div>
    <div id="tool2" class="tool">This is tool two.</div>
    <div id="tool3" class="tool">This is tool three.</div>
    </div>
    </body>
    </html>
     
    Jambalaya, Feb 11, 2006
    #2
    1. Advertising

  3. Jake Barnes wrote:
    > 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


    'AJAX interface' can mean all things to all men.

    > some online software. As an experiment, I built this page:
    >
    > http://www.publicdomainsoftware.org/ajaxExperiment.htm


    That is nice for you, and irrelevant to anyone reading news off-line.

    > Click anywhere to get the controls and then click
    > "Add paragraph" to add some text.


    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.

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


    Seems reasonable.

    > 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(refToBackgroundColorPicker);
    >
    > But now, as I understand it, the div "backgroundColorPicker"
    > is gone for good.


    So why do that? Why not re-append/insert the block of HTML to wherever
    you got it from in the first place?

    > 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("refToBackgroundColorPicker");


    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.

    > then I could create a function like this:
    >
    > function getRefToBackgroundColorPicker() {
    > var newRefToBgColorPicker =
    > refToBackgroundColorPicker.cloneNode();

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

    > return newRefToBgColorPicker;
    > }


    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.

    > Then I just have to use this function whenver I want
    > a reference to the div "backgroundColorPicker".


    It actually returns a clone of the DOM Node not a reference to the
    original.

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


    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.

    > Also, there is still the risk that I might
    > accidentally use removeChild() on the global variable,


    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.

    > destroying the only reference to that block of HTML,


    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.

    > and thus removing it from the
    > page for good.
    >
    > I realize, though, that there is another way to do this,


    Probably half a dozen other ways.

    > and, I think, it involves Javascript closures.


    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.

    > But I'm not sure how to quite do it.
    > I'd like to create a reference that is pretty much
    > indestructible.


    Closures are the nearest you will get to that.

    > A singleton.


    I am not user that is a useful label here.

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


    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.

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


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

    > {
    > arrayOfReferencesToHtmlBlocks[idOfHtmlBlock] =
    > document.getElementById(idOfHtmlBlock);
    > var htmlBlockRef = arrayOfReferencesToHtmlBlocks[idOfHtmlBlock];

    ^^^
    Multiple declarations of the same variable are pointless (though
    harmless).

    > }
    > var newHtmlBlockRef = htmlBlockRef.cloneNode(true);

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

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


    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.

    > I admit my conceptual grasp of closures is weak, and what
    > I've done here seems like it could be done without closures.


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

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


    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');

    > I suppose what I've done here does take me toward my goal.
    > I'd never use removeChild() on allHtmlReferencesObject,


    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.

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


    Richard.
     
    Richard Cornford, Feb 12, 2006
    #3
    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. jimmy

    survival of c++

    jimmy, Jul 11, 2004, in forum: C++
    Replies:
    56
    Views:
    1,481
    fabio de francesco
    Jul 21, 2004
  2. baalbek

    Survival of the fittest

    baalbek, Sep 27, 2006, in forum: Python
    Replies:
    9
    Views:
    287
    Dieter Maurer
    Oct 3, 2006
  3. =?iso-8859-1?q?Nicolas_Herv=E9?=

    gcc + gprof : basic bloc counting

    =?iso-8859-1?q?Nicolas_Herv=E9?=, Jul 25, 2007, in forum: C Programming
    Replies:
    2
    Views:
    357
    Richard
    Jul 26, 2007
  4. StephQ
    Replies:
    5
    Views:
    297
  5. RyanCarson

    Free AJAX Survival Kit

    RyanCarson, May 22, 2006, in forum: Javascript
    Replies:
    3
    Views:
    98
    Randy Webb
    May 22, 2006
Loading...

Share This Page