Introducing Prototype and Scriptaculous Part 2
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 allows us to rewrite this as simply as this:
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().
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
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.
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.
Page 3 of 4