Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this Javascript object not go out of scope after $(document).ready?

I have some working Javascript that manipulates the some DOM elements. The problem is, I don't understand why it works, which is never a good thing. I am trying to learn more about object oriented javascript and javascript best practices, so the organization may seems a little strange.

Basically, I wrap two methods that manipulate the DOM inside a CSContent object. I create an instance of that object, content in $(document).ready and bind some events to the functions in content. However, I am confused as to how these functions can still be called after $(document).ready exits. Doesn't that mean that content has gone out of scope, and its functions are not available? Anyway, here is the code:

function CSContent() {
    var tweetTextArea = document.getElementById('cscontent-tweet'),
        tweetTextElement = document.getElementById('edit-cscontent-cs-content-tweet'),
        charCountElement = document.getElementById('cscontent-tweet-charactercount');

    this.toggleTweetTextarea = function () {
      $(tweetTextArea).slideToggle();
    };

    this.updateTweetCharacterCount = function () {
        var numOfCharsLeft = 140 - tweetTextElement.value.length;
        if (numOfCharsLeft < 0) {
            $(charCountElement).addClass('cscontent-negative-chars-left');
        }
        else {
            $(charCountElement).removeClass('cscontent-negative-chars-left');
        }
        charCountElement.innerHTML = '' + numOfCharsLeft + ' characters left.';
    };
}



$(document).ready(function () {
    var content = new CSContent();
    //If the twitter box starts out unchecked, then hide the text area
    if ($('#edit-cscontent-cs-content-twitter:checked').val() === undefined) {
        $('#cscontent-tweet').hide();
    }

    $('#edit-cscontent-cs-content-twitter').change(content.toggleTweetTextarea);
    //Seems wasteful, but we bind to keyup and keypress to fix some weird miscounting behavior when deleting characters.
    $('#edit-cscontent-cs-content-tweet').keypress(content.updateTweetCharacterCount);
    $('#edit-cscontent-cs-content-tweet').keyup(content.updateTweetCharacterCount);
    content.updateTweetCharacterCount();
});
like image 339
jergason Avatar asked Aug 04 '10 19:08

jergason


3 Answers

This, m'lord, is called a closure: the local variable content will remain in memory after $(document).ready exits. This is also a known cause of memory leaks.

In short, you bind this function to an event listener of a DOM element and then the JavaScript garbage collector knows that it should keep the local variable intact. You can't call it directly (outside of the function), unless the event is triggered. With some, you can do this ‘manually’, if you really want to call the function afterward (e.g., using element.click() to simulate a click).

like image 66
Marcel Korpel Avatar answered Nov 15 '22 01:11

Marcel Korpel


I assume you wonder why the event handlers like

$('#edit-cscontent-cs-content-twitter').change(content.toggleTweetTextarea);

work?

Well you don't pass content as event handler but the function that is contained in content.toggleTweetTextarea. And this reference will still exist after content does not exist anymore. There is nothing special about it. You just assigned an object (the function) to another variable. As long as at least one reference to an object exists, the object won't be garbage collected.

Now you may ask why those functions have still access to e.g. tweetTextArea ? This is indeed a closure. When the functions are created via new CSContent(), the activation context of this function is added to the scope chain of the inner functions CSContent.toggleTweetTextarea and CSContent.updateTweetCharacterCount. So even if you don't have a reference to content anymore, the scope of this function is still contained in the scope chain of the other functions.

You won't be able to access the object contained in content anymore after ready() is finished, this indeed goes out of scope.

like image 35
Felix Kling Avatar answered Nov 15 '22 01:11

Felix Kling


My brain is off today, but shouldn't you be using closures in this situation?

$('#edit-cscontent-cs-content-twitter').change( 
    function(){ 
        content.toggleTweetTextarea(); 
    } 
);
like image 36
epascarello Avatar answered Nov 14 '22 23:11

epascarello