the open web, web development, and more

Wednesday, July 8, 2009

Better Objects Using jQuery

I've been working almost exclusively with JavaScript building single page applications recently. jQuery has made developing the apps enjoyable but one area I find a bit awkward is creating complex objects.

Classes work well but can be cumbersome when creating a user interface since the object reference returned from the constructor is not a DOM node but a JavaScript object. Therefore, in order to insert the user interface component into the page, a special method like getHTML must be available for that object. This isn't terrible but creates more complicated code. Here's an example user interface widget - a simple box displaying a name:

function Widget(name) {
var container = $("<div/>");

function setName(newName) {
name = newName;
container.html(name);
}

this.getHTML = function () {
setName(name);
return container;
};

this.getName = function () {
return name;
};

this.setName = function (newName) {
setName(name);
};
}

And the code that uses it:

var nameWidget = new Widget("Dave");
var widgetNode = nameWidget.getHTML();
$(document.body).append(widgetNode);

This isn't too bad but gets a bit ugly when you need the ability to hide, show, animate, or do anything interesting. The choice is either to attach more methods to the Widget class or work with the jQuery object returned by getHTML. Pattern-wise, adding more methods to Widget is the cleanest but could require creating a lot of methods. Working with the jQuery object returned by getHTML is easy but leads to managing two objects in your code: nameWidget and widgetNode

What would be really nice is the ability to call any jQuery method on the nameWidget object PLUS all the methods specific to the Widget class. It occurred to me that this could be possible with the addition of a small jQuery Plugin I've dubbed Methods.

(function ($) {

$.fn.methods = function (arg0) {
if (arg0)
{
return this.each(function (i) {
$(this).data("methods", arg0);
});
}
else
{
return $(this).data("methods");
}
};

})(jQuery);

This plugin adds the methods method to jQuery. A collection of functions can be passed to methods to attach it to a specific jQuery object. Calling methods then returns the collection of functions. Here's a rewrite of the Widget class to explain this better:

function widget(tag, name) {
var container;

container = $(tag)
.html(name)
.methods({
getName: function () {
return name;
},

// Return "this" for chainability
setName: function (newName) {
name = newName;
container.html(name);
return this;
},

// Add the end method for chainability
end: function () {
return container;
}
});
return container;
}

And the code that uses this:

var nameWigdet = widget("<div/>", "Dave");
$(document.body).append(nameWidget);

Now we have a nameWidget that directly corresponds to the user interface component and, using jQuery, can be inserted anywhere in the page. To access the object's specific methods, you simply call methods. Here's an example that shows the combination of the methods and chaining:

nameWidget
.hide()
.methods()
.setName("Bob")
.end()
.show()
.addClass("widget");

Since the Methods plugin uses jQuery's data method behind the scenes, the nameWidget can actually be retrieved through a DOM lookup.

var nameWidget = $(".widget")
.hide()
.methods()
.setName("Sally")
.end()
.show();

And that's it. In the end, Methods is a simple jQuery plugin that lets you create complex objects with custom methods and closures and can still be used like a regular jQuery object.

No comments:

Post a Comment