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();
});
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).
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.
My brain is off today, but shouldn't you be using closures in this situation?
$('#edit-cscontent-cs-content-twitter').change(
function(){
content.toggleTweetTextarea();
}
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With