Introducing Prototype and Scriptaculous Part 2
Adding event handlers
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.
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.
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.
Page 2 of 4