Introducing order to Aja
3.5 Third-party libraries and frameworks
A goal of most refactoring is reducing the amount of repetition in the codebase, by factoring details out to a common function or object. If we take this to its log- ical conclusion, we can wrap up common functionality into libraries, or frame- works, that can be reused across projects. This reduces the amount of custom coding needed for a project and increases productivity. Further, because the library code has already been tested in previous projects, the quality can be expected to be high.
We’ll develop a few small JavaScript frameworks in this book that you can reuse in your own projects. There’s the ObjectBrowser in chapters 4 and 5, the Com- mandQueue in chapter 5, the notifications frameworks in chapter 6, the Stop- Watch profiling tools in chapter 8, and the debugging console in appendix A. We’ll also be refactoring the teaching examples in chapters 9 through 13 at the end of each chapter, to provide reusable components.
Of course, we aren’t the only people playing this game, and plenty of Java- Script and Ajax frameworks are available on the Internet, too. The more estab- lished of these have the advantage of some very thorough testing by a large pool of developers.
In this section, we’ll look at some of the third-party libraries and frameworks available to the Ajax community. There’s a lot of activity in the Ajax framework space at the moment, so we can’t cover all the contenders in detail, but we’ll try to provide you with a taste of what sort of frameworks exist and how you can intro- duce order into your own projects by using them.
3.5.1 Cross-browser libraries
As we noted in section 3.2.1, cross-browser inconsistencies are never far away when writing Ajax applications. A number of libraries fulfill the very useful func- tion of papering over cross-browser inconsistencies by providing a common façade against which the developer can code. Some focus on specific pieces of functionality, and others attempt to provide a more comprehensive program- ming environment. We list below the libraries of this type that we have found to be helpful when writing Ajax code.
x library
The x library is a mature, general-purpose library for writing DHTML applica- tions. First released in 2001, it superseded the author’s previous CBE (Cross- Browser Extensions) library, using a much simpler programming style. It pro- vides cross-browser functions for manipulating and styling DOM elements, work- ing with the browser event model, and includes out-of-the-box support libraries for animation and drag and drop. It supports Internet Explorer version 4 upward, as well as recent versions of Opera and the Mozilla browsers.
x uses a simple function-based coding style, taking advantage of JavaScript’s variable argument lists and loose typing. For example, it wraps the common doc- ument.getElementById() method, which accepts only strings as input, with a func-
tion that accepts either strings or DOM elements, resolving the element ID if a string is passed in but returning a DOM element unmodified if that is passed in as argument. Hence, xGetElementById() can be called to ensure that an argument
has been resolved from ID to DOM node, without having to test whether it’s already been resolved. Being able to substitute a DOM element for its text ID is particularly useful when creating dynamically generated code, such as when pass- ing a string to the setTimeout() method or to a callback handler.
A similarly concise style is used in the methods for manipulating DOM ele- ment styling, with the same function acting as both getter and setter. For exam- ple, the statement
xWidth(myElement)
will return the width of the DOM element myElement, where myElement is either a
DOM element or the ID of a DOM element. By adding an extra argument, like so
xWidth(myElement,420)
we set the width of the element. Hence, to set the width of one element equal to another, we can write
Third-party libraries and frameworks 105
xWidth(secondElement,xWidth(firstElement))
x does not contain any code for creating network requests, but it is nonetheless a useful library for constructing the user interfaces for Ajax applications, written in a clear, understandable style.
Sarissa
Sarissa is a more targeted library than x, and is concerned chiefly with XML
manipulation in JavaScript. It supports Internet Explorer’s MSXML ActiveX com- ponents (version 3 and up), Mozilla, Opera, Konqueror, and Safari for basic func- tionality, although some of the more advanced features such as XPath and XSLT
are supported by a smaller range of browsers.
The most important piece of functionality for Ajax developers is cross-browser support for the XMLHttpRequest object. Rather than creating a Façade object of its own, Sarissa uses the Adapter pattern to create a JavaScript-based XML- HttpRequest object on browsers that don’t offer a native object by that name (chiefly Internet Explorer). Internally, this object will make use of the ActiveX objects that we described in chapter 2, but as far as the developer is concerned, the following code will work on any browser once Sarissa has been imported:
var xhr = new XMLHttpRequest(); xhr.open("GET", "myData.xml"); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ alert(xhr.responseXML); } } xhr.send(null);
Compare this code with listing 2.11 and note that the API calls are identical to those of the native XMLHttpRequest object provided by Mozilla and Safari browsers.
As noted already, Sarissa also provides a number of generic support mecha- nisms for working with XML documents, such as the ability to serialize arbitrary JavaScript objects to XML. These mechanisms can be useful in processing the
XML documents returned from an Ajax request to the server, if your project uses
XML as the markup for response data. (We discuss this issue, and the alternatives, in chapter 5.)
Prototype
Prototype is a general-purpose helper library for JavaScript programming, with an emphasis on extending the JavaScript language itself to support a more object-oriented programming style. Prototype has a distinctive style of JavaScript
coding, based on these added language features. Although the Prototype code itself can be difficult to read, being far removed from the Java/C# style, using Pro- totype, and libraries built on top of it, is straightforward. Prototype can be thought of a library for library developers. Ajax application writers are more likely to use libraries built on top of Prototype than to use Prototype itself. We’ll look at some of these libraries in the following sections. In the meantime, a brief discussion of Prototype’s core features will help introduce its style of coding and will be useful when we discuss Scriptaculous, Rico, and Ruby on Rails.
Prototype allows one object to “extend” another by copying all of the parent object’s properties and methods to the child. This feature is best illustrated by an example. Let’s say that we define a parent class Vehicle
function Vehicle(numWheels,maxSpeed){ this.numWheels=numWheels;
this.maxSpeed=maxSpeed; }
for which we want to define a specific instance that represents a passenger train. In our child class we also want to represent the number of carriages and pro- vide a mechanism for adding and removing them. In ordinary JavaScript, we could write
var passTrain=new Vehicle(24,100); passTrain.carriageCount=12; passTrain.addCarriage=function(){ this.carriageCount++; } passTrain.removeCarriage=function(){ this.carriageCount--; }
This provides the required functionality for our passTrain object. Looking at the code from a design perspective, though, it does little to wrap up the extended functionality into a coherent unit. Prototype can help us here, by allowing us to define the extended behavior as an object and then extend the base object with it. First, we define the extended functionality as an object:
function CarriagePuller(carriageCount){ this.carriageCount=carriageCount; this.addCarriage=function(){ this.carriageCount++; } this.removeCarriage=function(){ this.carriageCount--; } }
Third-party libraries and frameworks 107
Then we merge the two to provide a single object containing all of the required behavior:
var parent=new Vehicle(24,100); var extension=new CarriagePuller(12);
var passTrain=Object.extend(parent,extension);
Note that we define the parent and extension objects separately at first and then mix them together. The parent-child relationship exists between these instances, not between the Vehicle and CarriagePuller classes. While it isn’t exactly classic
object orientation, it allows us to keep all the code related to a specific function, in this case pulling carriages, in one place, from which it can easily be reused. While doing so in a small example like this may seem unnecessary, in larger projects, encapsulating functionality in such a way is extremely helpful.
Prototype also provides Ajax support in the form of an Ajax object that can
resolve a cross-browser XMLHttpRequest object. Ajax is extended by the
Ajax.Request type, which can make requests to the server using XMLHttp- Request, like so:
var req=new Ajax.Request('myData.xml');
The constructor uses a style that we’ll also see in many of the Prototype-based libraries. It takes an associative array as an optional argument, allowing a wide range of options to be configured as needed. Sensible default values are provided for each option, so we need only pass in those objects that we want to override. In the case of the Ajax.Request constructor, the options array allows post data,
request parameters, HTTP methods, and callback handlers to be defined. A more customized invocation of Ajax.Request might look like this:
var req=new Ajax.Request( 'myData.xml',
{
method: 'get',
parameters: { name:'dave',likes:'chocolate,rhubarb' }, onLoaded: function(){ alert('loaded!'); },
onComplete: function(){
alert('done!\n\n'+req.transport.responseText); }
} );
The options array here has passed in four parameters. The HTTP method is set to get, because Prototype will default to the HTTPpost method. The parameters
array will be passed down on the querystring, because we are using HTTPget. If
callback event handlers that will be fired when the readyState of the underlying
XMLHttpRequest object changes. The variable req.transport in the onComplete
function is a reference to the underlying XMLHttpRequest object.
On top of Ajax.Request, Prototype further defines an Ajax.Updater type of
object that fetches script fragments generated on the server and evaluates them. This follows what we describe as a “script-centric” pattern in chapter 5 and is beyond the scope of our discussion here.
This concludes our brief review of cross-browser libraries. Our choice of librar- ies has been somewhat arbitrary and incomplete. As we have noted, there is a lot of activity in this space at the moment, and we’ve had to limit ourselves to some of the more popular or well-established offerings. In the next section, we’ll look at some of the widget frameworks built on top of these and other libraries.
3.5.2 Widgets and widget suites
The libraries that we’ve discussed so far have provided cross-browser support for some fairly low-level functionality, such as manipulating DOM elements and fetch- ing resources from the server. With these tools at our disposal, constructing func- tional UIs and application logic is certainly simplified, but we still need to do a lot more work than our counterparts working with Swing, MFC, or Qt, for example.
Prebuilt widgets, and even complete widget sets for Ajax developers, are start- ing to emerge. In this section, we’ll look at a few of these—again, more to give a flavor of what’s out there than to provide a comprehensive overview.
Scriptaculous
The Scriptaculous libraries are UI components built on top of Prototype (see the previous section). In its current form, Scriptaculous provides two major pieces of functionality, although it is being actively developed, with several other fea- tures planned.
The Effects library defines a range of animated visual effects that can be applied to DOM elements, to make them change size, position, and transparency. Effects can be easily combined, and a number of predefined secondary effects are provided, such as Puff(), which makes an element grow larger and more trans-
parent until it fades away completely. Another useful core effect, called Paral- lel(), is provided to enable simultaneous execution of multiple effects. Effects
can be a useful way of quickly adding visual feedback to an Ajax user interface, as we’ll see in chapter 6.
Invoking a predetermined effect is as simple as calling its constructor, passing in the target DOM element or its ID as an argument, for example:
Third-party libraries and frameworks 109
new Effect.SlideDown(myDOMElement);
Underlying the effects is the concept of a transition object, which can be param- eterized in terms of duration and event handlers to be invoked when the transi- tion ends. Several base transition types, such as linear, sinusoidal, wobble, and pulse, are provided. Creating a custom effect is simply a matter of combining core effects and passing in suitable parameters. A detailed discussion of building cus- tom effects is beyond the scope of this brief overview. We’ll see Scriptaculous effects in use again in chapter 6, when we develop a notifications system.
The second feature that Scriptaculous provides is a drag-and-drop library, through the Sortable class. This class takes a parent DOM element as an argu- ment and enables drag-and-drop functionality for all its children. Options passed in to the constructor can specify callback handlers for when the item is dragged and dropped, types of child elements to be made draggable, and a list of valid drop targets (that is, elements that will accept the dragged item if the user lets go of it while mousing over them). Effect objects may also be passed in as options, to be executed when the item is first dragged, while it is in transit, and when it is dropped.
Rico
Rico, like Scriptaculous, is based on the Prototype library, and it also provides some highly customizable effects and drag-and-drop functionality. In addition, it provides a concept of a Behavior object, a piece of code that can be applied to part of a DOM tree to add interactive functionality to it. A few example Behaviors are provided, such as an Accordion widget, which nests a set of DOM elements within a given space, expanding one at a time. (This style of widget is often referred to as
outlook bar, having been popularized by its use in Microsoft Outlook.)
Let’s build a simple Rico Accordion widget. Initially, we require a parent DOM
element; each child of the parent will become a pane in the accordion. We define a DIV element for each panel, with two further DIVs inside that, representing the header and the body of each panel:
<div id='myAccordion'> <div>
<div>Dictionary Definition</div> <div>
<ul>
<li><b>n.</b>A portable wind instrument with a small keyboard and free metal reeds that sound when air is forced past them by pleated bellows operated by the player.</li>
of an accordion: accordion pleats; accordion blinds.</li> </ul> </div> </div> <div> <div>A picture</div> <div> <img src='monkey-accordion.jpg'></img> </div> </div> </div>
The first panel provides a dictionary definition for the word accordion and the sec- ond panel a picture of a monkey playing an accordion (see figure 3.9). Rendered as it is, this will simply display these two elements one above the other. However, we have assigned an ID attribute to the top-level DIV element, allowing us to pass a reference to it to the Accordion object, which we construct like this:
var outer=$('myAccordion'); outer.style.width='320px'; new Rico.Accordion( outer, { panelHeight:400, expandedBg:'#909090', collapsedBg:'#404040', } );
The first line looks rather curious. $ is actually a valid JavaScript variable name
and simply refers to a function in the core Prototype library. $() resolves DOM nodes in a way similar to the x library’s xGetElementById() function that we discussed in
the previous section. We pass a reference to the resolved DOM element to the Accordion object constructor, along with an array of options, in the standard idiom for Prototype-derived libraries. In this case, the options simply provide some styl- ing of the Accordion widget’s visual elements, although callback handler functions to be triggered when panels are opened or closed can also be passed in here. Fig- ure 3.9 shows the effect of styling the DOM elements using the Accordion object. Rico’s Behaviors provide a simple way of creating reusable widgets from common markup and also separate the content from the interactivity. We’ll explore the topic of applying good design principles to the JavaScript UI in chapter 4.
The final feature of the Rico framework to mention is that it provides very good support for Ajax-style requests to the server, through a global Rico Ajax- Engine object. The AjaxEngine provides more than just a cross-browser wrapper around the XMLHttpRequest object. It defines an XML response format that
Third-party libraries and frameworks 111
consists of a number of <response> elements. The engine will automatically
decode these, and it has built-in support for two types of response: those that directly update DOM elements and those that update JavaScript objects. We’ll look at a similar mechanism in greater detail in section 5.5.3, when we discuss client/server interactions in depth. For now, let’s move on to the next type of framework: one that spans both client and server.
3.5.3 Application frameworks
The frameworks that we have looked at so far are executed exclusively in the browser and can be served up as static JavaScript files from any web server. The final cat- egory of frameworks that we will review here are those that reside on the server and generate at least some of the JavaScript code or HTML markup dynamically.