The Javascript closure and the Module Pattern application.
There can be different and several ideas about which is the best way to use Javascript in our pages towards encapsulate behaviors and actions of the elements in the DOM, and sure there are diferent points of view about this. I think and believe as several people know that Module Pattern may be is the best way or at least the clearest . I usually make one file for each page within a Module Pattern named as the page.
Summarizing, the main idea of these examples is shows a way or “right” way to use Javascript and no matter which library or framework ( Jquery, Mootools, YUI, etc ) are you using. It is using closures in Javascript and the Module pattern to encapsulate the behaviors and functionalities of a given page.
The Javascript Closure.
I’m not going to suppose you have knowledge in Javascript Closures, in fact I’m going to define them and show you some simple examples of this powerful tool, or better, behavior.
See the following definition (variable scope).
A closure are the local variables for a function – kept alive after the function has returned.
It is simple to understand, the local variables still are in the variables stack and can be access after the function returns, the good way to understand always is by examples, please see the following examples.
var a = function(param){
var internalParameter = param;
return function(){
alert(internalParameter);
} ;
}
var b = a(9);
b(); // Shows 9.
As you can see in the previous example (you should see the number 9), the returned function still has access to the variable internalParameter when the function “a” has returned and the alert will be 9 as we would expect , this is one of the basic examples of the use of closures and this is the beginning of understanding and using the Module Pattern, in fact Module Pattern is one of the bigest example of closure in javascript.
By the way before we continue with the pattern I will show you some other examples of closure.
function add(x) {
var temp = 10;
return function addToReturn(y) {
alert( x + y + (temp++));
};
}
var a = add(10);
a(10);
a(10);
a(10);
The previous example will give us 3 alerts, will be 30, 31 and 32 and the variable temp is our closure in this case.
function addButtons(numOfButtons) {
var i, button;
for(j = 0; j < numOfButtons; j++) {
button = document.createElement("button");
button.innerHTML = "Button " + i;
button.onclick = function() {
alert('You just clicked Button, buttons count ' + numOfButtons );
};
document.body.appendChild(button);
}
}
addButtons(6);
The previous code will add 6 buttons in the document and each button will have an event, when you click will show you just the number 6.
The Module Pattern.
Now, whe’re going to define and make some examples (I’ ll try to be clear) about Module Pattern, the most clear definition I have found is “The Module Pattern means you define a variable as an anonymous function that gets immediately called with (). You define private functions and variables and return your public variables and functions as properties and methods of an anonymous object“.
To summarize the definition in a more clear way, this pattern allows us to scope variables and methods with both private and public visibility, bellow I’m going to create an example to figure out the pattern, please see the following example being “page” the name of the page (would be great if you use the name of the page as the name of the module pattern , is a good custom).
/* page.html */
<html><head><title>My page</title></head></html>
/* page.js */
var page = function() {
var helloWorld = function() {
alert('Hola mundo');
}
/* Public interface object */
var oPublic = {
init: function() {
helloWorld();
}
};
return oPublic;
}();
$(document).ready(function(){
page.init();
});
If you take a look at the previous code, the variable page has parentheses at the end of its declaration, it means the function is “auto executed” and the object with the public interface is returned.
In this case for example, when the page has been loaded the method init() is executed, the variable page is the object returned by the first function and if you remember the definition of closures in javascript the method init() still has access to the function hellowWorld which was declared into the first function, it is really clear and powerful whether we want to handle or add events, add delegations, add elements etc when the page is loaded, all the functionality and functions will be into the closure and looks like as “private” that will be called for public functions or methods in the public interface (oPublic).
Please to clarify take a look at the following example.
var myObject = function(){
var privateVar = 1;
function privateFunction(){
alert('Hi, Im a private function called by the public function ');
};
function publicFunction(){
privateFunction;
}
return {
publicFunction:publicFunction,
publicVar:publicVar,
}
}();
Other more real example (I dont want you to understand all the example, is only to see a little example in real use)
var pageComments = function() {
var createReplyArea = function(elContainer, nCommentId) {
var sHtml = 'textArea and buttons to add or cancel the new reply';
$(elContainer).html(sHtml);
};
var textAreaCountdown = function(elTextArea, elRemainingChars) {
var sTextContent = $(elTextArea).val(),
nCharsCount = sTextContent.length,
nRemainingChars = 512 - nCharsCount;
if(nCharsCount > nMaxCharacters) {
$(elTextArea).val(sTextContent.substring(0, nMaxCharacters));
$(elRemainingChars).text(0);
} else {
$(elRemainingChars).text(nRemainingChars);
}
};
/* Public interface object */
var oPublic = {
var init: function() {
$('comment-container').delegate('a.reply', 'click', function(event) {
event.preventDefault();
event.stopPropagation();
var elTarget = event.currentTarget,
elContainer = $(elTarget).parent().find('div.comment-reply-wrapper'),
nCommentId = $(elTarget).parent().find('input').val();
if(!$(elContainer).children().length) {
createReplyArea(elContainer, nCommentId);
}
});
$('div.comment-widget').delegate('textarea', 'keyup keypress change paste select', function(event) {
var elTarget = event.currentTarget,
elRemainingChars = $(elTarget).parent().siblings().find('span.remaining-characters');
textAreaCountdown(elTarget, elRemainingChars);
event.stopPropagation();
})
}
};
return oPublic;
}();
$(document).ready(function(){
pageComments.init();
});
When the page has been loaded the public init function is called and adds the handle of the click event to add the new reply area on each “a.reply”, also each text area has handled the key events to count and limit characters that we enter, you could see a clear example of closure and module pattern because the function returns the public object with the init() function and the createReplyArea and textAreaCountdown function are not public but, the element of the DOM still have access to these functions by the events after the function has returned.
However there are different ways to do the module pattern, for example different ways to write the object to return, for example see the following declaration.
var f = function() {
var obj = {};
obj.methodOne = function(){ alert(privateVar); }
return obj;
}();
And the last example to close the idea, you have a page named liks.html with one DIV (class=”link-container”) within links (class=link) and you want to handle for each link the “click” event.
/* page.js */
var linksPage = function() {
var handleLink = function(target) {
alert(target);
}
/* Public interface object */
var oPublic = {
init: function() {
$('link-container').delegate('a.link', 'click', function(event) {
event.preventDefault();
event.stopPropagation();
handleLink(event.currentTarget);
}
/* May you want to do the same with mootools
$('link-container').addEvent('click:relay(a.link)',
function(event, clicked){
});
*/
}
};
return oPublic;
}();
$(document).ready(function(){
linksPage.init();
});
This code will handle the click event in all links (class=”link”) into the page, and when you click whichever link will show you an alert with the link-object.
This is all for this moment, see you in the next tutorial.
In the next step are going to take a look at the delegations in mootools and jquery. Wait and see.