Introducing Prototype and Scriptaculous Part 2

Friday Mar 9th 2007 by Dave Crane and Bear Bibeault with Tom Locke
Share:

See how Scriptaculous makes use of Prototype to provide a number of higher-level widgets and components, mostly aimed at creating user interfaces.

Editor's Note: This piece picks up where the article Introducing Prototype and Scriptaculous Part 1 left off.

What is Scriptaculous?

Prototype provides an extremely solid foundation for developing complex, well-structured code, but on its own does little beyond that. The onus of developing the functionality that the end user will see still rests firmly with the developer.

Scriptaculous is a library that makes use of Prototype to deliver a rich set of high-level functionality that the developer can put to work directly in creating polished interactive user interfaces. On its own, Prototype smoothes out many of the wrinkles of Ajax development. When used with Scriptaculous, it transforms the way in which we approach the web user interface, by making features such as animation and dragging and dropping as simple as a few lines of code.

Like Prototype, Scriptaculous covers several distinct areas. Let's look at each briefly in turn.

Visual effects

It is common when writing any computer application to wish to draw the user's attention to some part of the interface, in order to provide visual feedback. A button may wish to announce that it is clickable when the mouse moves over it. Lists of items may wish to notify the user when new items arrive or old ones vanish, particularly if it is the result of a background process. It's easy to overdo this type of functionality and end up with a user interface that distracts users or gets in their way, but such effects, if well done, can make an application more pleasant to use. In the world of web applications, in which a user may go elsewhere with a few keystrokes, making an application easy to use is imperative.

Scriptaculous makes it easy to create visual feedback of this type through its Effects library. This library is remarkable not only for the quality and range of effects that it enables, but for the high quality of the underlying design of the code, which makes it easy to compose multiple effects, run arbitrary code before, during, and after the effect, and synchronize effects with one another. Needless to say, this good design is made possible by the language features provided by Prototype.

In addition to being directly available to the coder, the Effects package is used within Scriptaculous to add visual feedback to the other main packages. Let's look at them now.

Drag and drop

Before Ajax, clicking on hyperlinks or submitting HTML forms could only initialize requests to the server. Now that requests can be fired programmatically, other types of user interaction can be used to trigger requests too, so a wider range of user interaction techniques are finding their way into web applications.

Dragging and dropping is a common user interface metaphor in desktop applications, and in many cases it provides the most convenient and intuitive way of interacting with a computer. The DOM has no direct support for drag-and-drop events, and implementing drag and drop in JavaScript means relying on nothing more than mouse click and movement events.

The good news is that Scriptaculous implements a feature-complete drag-and-drop system that can be applied to most types of DOM element with relatively little code. The look and feel of the interaction can be customized using the Effects library, and custom event handlers provide callbacks for all stages of the drag-and-drop event.

Components

The features that we've discussed so far are frameworks that can be used to enhance a coder's application. Scriptaculous also provides a number of complete widgets, in the Components library. At the time of writing, the Components library contains two components: the Auto- Completer can attach a predictive drop-down element to any text field, which can endow an ordinary HTML Form element with features similar to Google Suggest; the in-place editor allows any DOM element to transform itself into a text input field and back again.

In addition to these high-level components, Scriptaculous provides a few helpers and utilities of its own. We'll conclude our initial review of the library with a look at these.

Utilities and Testing Frameworks

Scriptaculous provides some further extensions to the core JavaScript objects and DOM that are mainly concerned with easier manipulation of the user interface. These build on top of the extensions defined by Prototype.

In addition, Scriptaculous provides a complete unit-testing framework that runs inside the browser. This is designed mainly for internal use by the Scriptaculous development team, as the entire library is well covered by tests, but it can be used as a standalone testing library too.

This concludes our initial review of the Prototype and Scriptaculous libraries. Before we look in more detail at the features of each in subsequent chapters, we'll quickly demonstrate what Prototype and Scriptaculous can do to help an ordinary Ajax application.

Applying Prototype and Scriptaculou

Writing Ajax applications by hand requires an intimate knowledge of JavaScript's language features. To those of us coming to Ajax from server-based web coding, familiar with languages such as Java, PHP, and C#, many of these features are rather exotic. Worse, we will need to master the subtleties of cross-browser incompatibilities. Prototype and Scriptaculous have many language and cross-browser features built in, and they can help to ease the pain a great deal.

In this section, we'll look at a simple Ajax application that allows a user to assign a rating to an article (or a tune, picture, or anything else). We'll show how to code the app by hand, and then refactor it using Prototype and Scriptaculous to simplify a few knotty bits of JavaScript and add a few quick wins too. So, without further ado, let's have a look at the Ratings application.

Introducing the Ratings example

The Ratings example is a simple widget built using Ajax and DHTML techniques that can be easily inserted into a web page to show an interactive display of the rating that the user has assigned to an item. Figure 1.1 shows the widget's visual components.

Figure 1 Ratings widget embedded in a web page. The small arrow icons are buttons allowing the user to increase or decrease the number of stars assigned to an item-in this case an article. (Icons are from the "Nuvola" icon set by David Vignoni, http://www.icon-king.com.)

Operating the widget is simple enough. The user can increase or decrease the rating by clicking on the blue arrow buttons with the mouse, and the number of stars is incremented or decremented, within the range of zero to five. When the user changes the rating, the widget also makes an asynchronous call to the server to update the central records. We won't worry for now about what the server does with that information, or what the response is.

That's our brief, then. As coders, we're more interested in getting under the hood and seeing how the implementation is done. Let's run through a few highlights of the code now. The complete code for the app is available for download from the Manning web page for this book.

Using the component

We have striven to follow best practices in writing the app, and we tried to make it simple for designers to use without having to write a lot of JavaScript themselves, or to prepare a complex HTML template. As a result, the Ratings app creates all of its user interface programmatically, and the designer needs only specify an empty HTML element within which it will sit. Needless to say, we've separated our various resources out nicely, providing a .js file containing all the code, a CSS stylesheet, and a set of images.

To incorporate the widget into a page, then, the designer needs to add a few lines of code in the header of the page:

The Rating object is defined in the JavaScript library, which we'll look at shortly. The constructor for the object takes two arguments: the ID of the HTML element in which it will render itself, and a caption to be displayed. We've specified a target element called myRating, so we'll define this in the HTML like so:

<div id='myRating'>

That's everything that our design team needs to do to use the widget. Let's see what goes on behind the scenes, when the widget is activated.

Initializing the user interface

The constructor function for the Rating object is relatively simple, taking a note of the main arguments and then delegating to a second function updateUI(). Listing 1 shows the code for these functions.


There is quite a lot going on here, so let's pick through it piece by piece. The updateUI() method takes an optional argument delta, which we'll ignore when we initially call it in the constructor . It then proceeds to build up a set of HTML markup as a string, defining the UI that we saw in figure 1 as an HTML table. This string is then assigned to the target element using the innerHTML property .

We go on to assign event handlers to the buttons. The next line looks quite strange-we are defining a variable called rating that is a direct reference to this . The reason for this is rather arcane. We are going to create a closure when we define the event handler, and the special variable this cannot be passed in to a closure. We create rating as a copy in order to pass it in.

In the next few lines we're back on fairly safe ground. We've added unique IDs to our buttons in the big innerHTML string that we just wrote, so now we can programmatically reference the button elements .

Adding event handlers

We go on to define an event handler for the entire widget ( in listing 1). This is an anonymous function, defined inline. Because of the way the JavaScript event model works, when the function is called, the variable this will no longer refer to the Rating object, but to the HTML element that fired the event. We refer to the variable rating, which is bound to the function as part of the closure, in order to see the Rating object inside the event-handling code.

It's a common mistake to refer to this inside event handlers, and writing this code took a few goes to straighten all the ratings and thises out, but we've done it. When the buttons are pressed, we re-render the entire UI by calling updateUI() again, this time with the delta argument to indicate that the rating is going up or down.

Finally, we tell the server that the rating for this item has changed . We'll be using core Ajax techniques to do this, and we'll look at these in a minute. First, let's review what we've done in order to get our two buttons up and running. We've visited a number of unusual language features in JavaScript, including the ability of Function objects to be called with arbitrary contexts (i.e., the variable that evaluates to this within the function), and the ability of Function objects to create closures implicitly. Both of these require quite a deep understanding of the language, which is fine if we like collecting unusual programming languages, but if we're a Java or PHP coder seconded into doing a bit of Ajax work, it's quite a lot to take on board.

We'll soon see how Prototype and Scriptaculous can help to keep the language out of our hair. First, let's have a look at the Ajax code.

Making an asynchronous HTTP call

We're going to use the XMLHttpRequest object to contact the server whenever the user clicks one of the buttons. As we noted earlier, XMLHttpRequest confers the ability to work with the HTTP protocol at quite a low level, and it is consequently not very straightforward to use for extremely simple tasks. There are several wrappers for the XMLHttpRequest available now, and we'll see the one provided by Prototype in a minute. To emphasize the difference, though, we're going to use the raw XMLHttpRequest in this example. Listing 2 shows the code required to do so.



Again, the code required to do the job isn't that small. Let's pick through the main points. First, we need to get hold of an XMLHttpRequest object. In some browsers it's a native object, and in others it's an ActiveX component, and we try to account for all the possibilities . By luck, we've got it the right way around here, testing for a native object first. Internet Explorer version 7 has arrived upon the scene, and it supports a native XMLHttpRequest, as well as ActiveX for backwards compatibility. If we'd tested for ActiveX first, we would have ended up using ActiveX unnecessarily under IE 7, and potentially blocking browsers where ActiveX controls have been locked down, but our Ajax code would otherwise have worked. We could have supported older versions of IE by checking for alternative ActiveX types too, but that's a lot of background knowledge required to implement a simple rating widget, so users of IE 5.5 are maybe out of luck with our app.

The second point to note is that strange closure trick again. We define the variable rating and then refer to it inside the event handler . In this case, the event handler is simply a one-line call to another function, which might leave us wondering why the onReadyState() function wasn't assigned directly if we didn't understand the intricacies of implicit closures in JavaScript.

We're calling a server-side process that talks in terms of standard querystring key-value pairs. Almost every server-side language provides automatic parsing of querystrings, but with XMLHttpRequest, we need to build up the string manually , remembering to call encodeURI() for each value. We then need to set a few crucial HTTP headers before we're ready to send our request out . It's a little-known fact that the convenience methods we're used to on the server, such as Java Servlet's request.getParameter() and PHP's $_GET array, will only be populated if the request has a content type of application/x-www-form-urlencoded. HTML forms fill this in for us automatically, but with XMLHttpRequest, we need to do it ourselves.

Once the request goes out, our callback handler is busy. Rather than being called once when the request completes, it is notified at various stages in the lifecycle of the request, which is great for implementing progress bars, but something of an overhead for us here. A readystate value of 4 corresponds to a completed request, so we simply check for that and then call either the success handler or the error handler depending on the HTTP code of our response.

Let's review what we've been through here. Once more, everything is done, and it works, but we've gone to rather a lot of effort, and we've needed rather an intimate knowledge of the HTTP protocol, particularly concerning how querystrings are encoded in requests and are only decoded on the server if the right set of HTTP headers is applied. Furthermore, we've had to get our heads around closures once again. After writing this application, I refactored the code to use Prototype and Scriptaculous features in a few places, to make it easier to work with. Let's look at how it simplified things.

Adding Prototype and Scriptaculous

We can see the first advantage of using Prototype and Scriptaculous before we open the files. The main code file, ratings.js, is roughly 20 percent smaller as a result of our work, which certainly suggests that these libraries can help our productivity. However, that in itself doesn't tell us very much. Let's step through the refactoring and look at the details.

DOM helper methods

Prototype comes equipped with a range of helper methods that make it easier to work with the DOM. The simplest of all is a function named simply $() (and, yes, that is a valid name for a variable in JavaScript).

In our original code, we've been looking up DOM elements by their IDs, like this:

this.body=document.getElementById(divId);

Prototype allows us to rewrite this as simply as this:

this.body=$(divId);

For now, it's enough to know that we've saved ourselves from some tedious typing. In fact, $() is a lot more powerful than document.getElementById().

Event handlers

In the original version of the code, we had to get our heads around closures and function contexts when writing the event handler for the buttons (see in listing 1). This had two practical consequences. First, we had to define the event-handler code inline as an anonymous function, in order to get the closure to work. Sometimes it's good to use anonymous inline functions, but here we did it because we had no choice. Second, we had to refer to the Rating object as rating rather than this inside the event-handler code, which felt a bit odd.

While writing the event handler, we also had to write some crossbrowser code to handle the different ways of obtaining the event object and its target element.

Prototype can relieve us of all these chores with a single call. Function. bindAsEventListener() wraps an ordinary function up as an event handler, creating the closure for us, sorting out the function context issues, and presenting us with a robust, cross-browser event object whose target element we can readily access. Listing 3 shows the modified code, with changes shown in bold.

We're using two event-handler features to get around differences in the cross-browser event models here. Event.observe() allows us to attach multiple handler functions to an HTML element safely. Function. bindAsEventListener() turns an ordinary function into an event handler, supplying it with a cross-browser event object, and passing in the first argument as the event context. Here, we've passed our Rating object in, which means that we can define our event-handling code as an ordinary member function of the Rating prototype .

The clickHandler() function that we define contains the same logic as its anonymous predecessor but is certainly easier to read. We can access the HTML element that fired the event in a single line of code , and we refer to the member variables of the object in a natural way using this.

There is an even bigger win to be made, though, when sending the Ajax request to update the server. Let's look at that next.

Ajax requests made easy

Coding the Ajax request required not just a detailed knowledge of JavaScript language internals, but of the HTTP protocol too. Knowledge is good, but we were in a hurry, and figuring out the details slowed us down rather a lot.

Prototype provides a utility object, the Ajax.Request, which encapsulates a lot of this complexity so that we don't have to in a simple case like this (but we still can when we're in HTTP power-coding mode!). Listing 4 shows the revised Ajax code.


When writing our own Ajax call, we had to manually encode and create a querystring (see in listing 2). Here, we're using Prototype's $H() function (yes, these guys like short names!) to create a Hash object out of an ordinary JavaScript object . Suffice it to say that Hash has a predefined toQueryString() method that will create our parameters for us. The syntax may look a little odd at first, especially if you don't realize that $H() is just a JavaScript function, but once you get it, it saves a lot of trouble in remembering to call encodeURI() every time you create a querystring.

Now on to the biggest improvement in the coding-making the request. The Ajax.Request object takes two arguments in its constructor . The first is the URL on the server, and the second is a JavaScript object stuffed full of optional arguments. (We'll see this idiom a lot throughout this book, in the predefined objects and in our own code.) We specify the HTTP method as POST, and pass in our querystring. Details such as mime type will be taken care of for us.

We also add in two callback handlers. Ajax.Request allows us to define suitably high-level callbacks, for success and failure of the request . Note that we're using Function.bind() here. Like bindAsEventListener(), this simply ensures that when the callback is invoked, it will have the context object that we want it to have.

We no longer need to write a callback that will be invoked multiple times during the request, because in this simple case, we don't need to. Prototype handles all that and the HTTP response codes for us. Compared to listing 2, tellServer() has shrunk by at least 50 percent, and we've got rid of onReadyState() altogether.

We could rest on our laurels right now, but let's make one final change to the application before we wrap up this chapter, and show off one of Scriptaculous's useful features.

Adding Polish to the User Interface

Our refactored app is matching the original version feature for feature with less effort. While coding up the original app, we thought of several "nice to have" features that there just wasn't time to implement. Well, we've saved ourselves a little bit of time now, so let's have a look at one of them.

When the user modifies the rating, there is no visual feedback beyond the star disappearing. A little animation might make the app more satisfying to use, and might ultimately increase uptake of the project. This sort of benefit is hard to gauge, though, so we don't want to spend too much effort on it, lest it distract us from more serious matters.

Using Scriptaculous's Effects library, we can animate the adding and removing of stars in only a few lines of code, as shown in listing 5. (We've reproduced the entire function here-the new code is highlighted in bold).

In the previous versions of the application, we always corrected the rating score before rendering. In this case, when we decrement, we want to initially draw in one extra star, and then get rid of it, so we only correct the rating beforehand if the score has gone up . After everything has rendered, we then set up the effects and decrement the score if necessary .

The effects themselves are ridiculously easy to use, requiring only the ID of the element to operate upon. We therefore need to provide ID attributes for our images , but that's a minor hardship. The constructors can, like Ajax.Request, also accept an object full of options.

By adding these few lines of code, a newly improved rating will flash on and off briefly to announce its presence, and a reduced rating will see the leftmost star drop off the bottom of the page. It's hard to present this as a still picture in a book, unfortunately, but you can download the code and see it for yourself.

That concludes our first taste of what these libraries can do for us. In subsequent chapters, we'll dig more methodically into this rich seam.

Summary

In these two articles, we looked at the development of Ajax technologies. As long ago as the late 1990s, preliminary explorations of asynchronous communication between the browser and server were being undertaken, mostly in isolation. In early 2005, the coining of the name Ajax provided a much-needed focal point for these explorations. Since then, there has been a major drive towards increasing the ease of development of Ajax, in which frameworks and libraries are playing an important part. It is no longer sufficient to be able to make an Ajax app work; we now need it to work smoothly and robustly, and it must be easy to develop and maintain.

We introduced Prototype and Scriptaculous, two best-of-breed JavaScript and Ajax libraries, and we looked at their main purposes and constituent pieces. Prototype is a relatively low-level library, providing additional language features and facilities. Scriptaculous makes use of Prototype to provide a number of higher-level widgets and components, mostly aimed at creating user interfaces.

Both libraries use a number of advanced JavaScript techniques internally, and even introduce some new advanced features of their own. However, using these libraries in the most straightforward way does not require a detailed understanding of these capabilities. We introduced a simple Ajax example app and refactored it using Prototype and Scriptaculous. Internally, the libraries are complex, and reading their source code, one might suppose that a degree in rocket science is necessary to understand them. However, our refactoring showed this not to be the case, and we firmly believe that the average coder can make quick wins by using these libraries. We'll continue to explore the libraries in more depth throughout this book, and demonstrate how you can benefit from their raw power.

About the Authors

Dave Crane is an Ajax authority and lead author of the best-selling Ajax in Action. He is currently senior developer for UK-based Historic Futures Ltd., developing the next generation of socially responsible supply-chain systems using Ajax to link rural cooperatives and multinational corporations.

Michael "Bear" Bibeault is a US-based Java programmer with over 20 years' experience in enterprise and Web applications. He's a popular moderator on The Java Ranch and coauthor of the upcoming Ajax in Practice.

Tom Locke is an UK-based independent web developer and trainer specializing in Ruby on Rails. He is the creator of Logix, a multi-language programming system, and the CTO of LiveLogix.

Source of This Material

Prototype and Scriptaculous in Action
By Dave Crane and Bear Bibeault with Tom Locke
ISBN: 1-933988-03-7
Published by Manning Publications Co., March 2007
Paperback: 544 pages, Retail price: $49.99
MEAP + Softbound print + ebook - $49.99
MEAP + ebook only - $25.00
This material is Chapter 1 of the book.
Reprinted with the publisher's permission.






Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved