Closing the Loop

I'm not the first person to talk about how sweet closures are. I'm assuming you know about them already. Javascript, the rinky-dink language most people associate with toggling web page elements, has them. Recall that Javascript also lets you modify the DOM. This means that you can do all the totally awesome stuff that closures get you with objects in your web page.

I'll give an example of some of this sweetness where I'll be using the prototype.js framework. You don't need to know anything about it other than it facilitates OOP in javascript*.

Here's the basic idea of my demo: You've got this really complex web app and you want to build a twitter-like service on top of it. Let's call it Tweeter (don't sue me!). Lets say it's an enterprise web app and you want your company to be able to tweet with their coworkers. Lets also pretend that a mashup with twitter isn't an option -- maybe this is the NSA or something. Just pretend.

Product decided that Tweeter will suck unless lots of people use it, and that nobody will use it unless they can tweet all over the existing app. They want to tweet anytime anywhere. This is a web app, remember, so this means building divs and spans in like 50 places or something, right?

Of course not! You scoff at such silliness! You're a smart web developer -- you know that if you write a function called makeTweeterInput() that prints the divs and spans on your web server you can just call makeTweeterInput() instead of writing out the divs and spans in all 50 places. You're so smart!

But you start using makeTweeterInput() and you run into a problem when you have more than one on the page: You gave those divs and spans ID's so javascript could access the divs and spans correctly, but now you have multiple divs and spans. If only there was some environment where variables could be looked up by name! If only there was some way those divs and spans knew where they came from.

In my previous post I talked about how you could use closures to make sure you were referring to the right this. Now I'm gonna show you how nice it is using closures to make sure you refer to the right DOM objects. (note: this relies on prototype.js's handy Class.create())



var Tweeter = Class.create({
wrapperDiv: null,

initialize: function() {
this.wrapperDiv = new Element('div');

this.tweeterText= new Element('textarea');
this.wrapperDiv.insert(this.tweeterText);

this.submitButton = new Element('input', {
'type': 'button',
'value': 'tweet this'
});
this.submitButton.observe('click', this.submitTweet.bind(this));
this.wrapperDiv.insert(this.submitButton);
},

submitTweet: function() {
this.wrapperDiv.update('submitting...');
someAJAXCall(this.afterSubmit.bind(this));
},

afterSubmit: function() {
this.wrapperDiv.update('tweet totally submitted');
}
});


Now, all I have to do to put this into a web page is create one and drop it in.
var tweeter = new Tweeter();
var someDiv = document.getElementById('someID');
someDiv.insert(tweeter.wrapperDiv);


Each object created will know precisely which object it is on the page because closures have literally bridged the gap between the javascript variable environment and the DOM.

See this in action. (edit: this web server has since been killed)







PS: You might cringe at the idea of creating DOM objects in JS. The performance really isn't an issue**. I know of at least one app on a prominent social network that has an active user base in the millions that uses this method.


* If you regularly write in javascript, you probably should get to know it.


** Technically, I think that if you don't ever reload a webpage and just keep adding objects with closures via javascript you will leak a little memory, but at a pretty slow rate. Closing the browser, web page, or refreshing the page will take care of it. In short this pretty much isn't an issue.

No comments: