Script.aculo.us Controls: Do Your Web Users a Favor

n a previous DevX article, I wrote about the JavaScript framework Prototype, which aims to ease the development of dynamic web applications. That article ended with just a few words about Script.aculo.us, which is a fantastic UI library based on Prototype. This article gives Script.aculo.us its due by examining the web controls it provides: autocompleters, sliders, and in-place editors.

Script.aculo.us is a pretty big library, so no single article can cover it completely. However, the knowledge you will gain will enable you to leverage the power of Script.aculo.us web controls to improve the end-user experience.

Setting Up the Environment
First of all, point your browser to the Prototype Home Page and download the latest version. (At the time of writing, it was the 1.6.0.2.) The only file you really need is prototype.js. Next, download Script.aculo.us. Go to its download page and get the latest version. (At the time of writing, it was the 1.8.1.)

Uncompress the archive and place the files you find under the src directory?along with prototype.js?under a directory of your choice. I put them under the scripts directory, which I’ll refer to throughout the rest of the article.

In order to use Script.aculo.us controls in your applications at this point, you just need to include the following script files in your pages using code similar to the following:

As you can see, you need to include both effects and controls in order to use the latter. This is due to the fact that controls use the effect part of the library internally.

With the environment set, you can begin exploring the fantastic world of Script.aculo.us controls. I suggest you start with the most used one: autocompleter.

Author’s Note: You may have noticed that Script.aculo.us is more modular than Prototype. As a matter of fact, you can include only the part of the library you’re interested in, instead of the whole bundle of code. However, if you need the Script.aculo.us library in its entirety, you can include it all by using the following syntax:

So, What Is an Autocompleter Anyway?
If you don’t know what an autocompleter is, I suggest you go to the WeeFly Home Page to see it in action. WeeFly is an innovative low-cost flight and hotel search engine. Its very first page contains two text boxes, one for departure airports and one for arrival airports. Type at least three characters of an airport name into the departure airports field. You should see a dropdown list of airport names that contain those characters. Figure 1 shows this scenario using “MIL” as a search string.

Figure 1. WeeFly Autocompleter for the Departure Airport: Type at least three characters (“MIL”) of an airport name into the departure airports field and see a list of matches.

By the end of this article, you’ll be able to build such an autocompleter too.

As you may have noticed, an autocompleter is formed by a text box and a hidden

, which is shown as soon as the user types some characters into the text box. Of course, before displaying the

, the autocompleter must provide a list of items that match the user request. But how are these items retrieved? Basically it uses one of two retrieval options depending on where the items reside:

  • If they reside on the server, the autocompleter retrieves them using Ajax.
  • If they are stored on the client, the autocompleter retrieves them within a JavaScript array.

Let’s walk through both options.

Using Ajax-based Autocompleter
This type of autocompleter works as follows: the user types a given number of characters and an asynchronous request starts in order to retrieve data that matches the string typed by the user. Of course, you’re free to use JSP, Servlet, ASP.NET, or whichever language/technology you feel comfortable with to write the server side. For this article, however, I use PHP.

From the Script.aculo.us point of view, you can create an Ajax-based autocompleter using the Ajax.Autocompleter class. Here is an example:

new Ajax.Autocompleter(     'autoCompleteTextField',     'autoCompleteMenu',     'countries-list.php',     {          minChars: 1     });

The parameters you pass to the constructor are:

  • The name of the text box where the user types the characters
  • The ID of the
    that has to be filled with the list of items
  • The URL of the server-side component to which the request must be sent
  • An optional literal object, which indicates the available options for customizing the autocompleter (more on this later)

Don’t worry if this is not completely clear. The following example will explain how the whole thing works. Suppose you want to build an autocompleter for a list of countries. Listing 1 shows the code you need to accomplish this task.

You can find the Listing 1 code in the ajax-autocomplete.htm file. The CSS section gives the

a similar look to WeeFly. (Getting into the details of the CSS code is out of the scope of this article.)

The section is very short. It contains a label, a text box, and a

that will be filled with the matching items. The core function is buildAutcompleter, which is called when the window is loaded (thanks to the Event.observe method provided by the Prototype framework). As you can see, buildAutocompleter creates an Ajax-based autocompleter and attaches to it the text box, the

, and the component to call in order to retrieve the data. Ajax.Autocompleter expects a server response with the following format:

  • Item1
  • Item2
  • ...

As you can see, the server response is a simple HTML unordered list. As a matter of fact, Listing 2 contains the code for countries-list.php, a file included in the downloadable code for this article.

Figure 2. Ajax-based Autocompleter for the Countries: Here is a screen rendering of the countries-list.php PHP script.

The above PHP script retrieves the string typed by the user and builds an unordered list of the countries that match the user request. Note that the country list is hard-coded within the countries.inc.php file. In a real-world application, this list would be stored in a database, XML file, or some other more flexible data store. Figure 2 shows a screen rendering of the above PHP script.

OK, that’s all well and good, but what if your server response is XML or JSON instead of a simple HTML unordered list? Don’t worry; by the end of this article you’ll know how to develop an autocompleter?based on the one provided by Script.aculo.us?that lets you handle any type of response. For now, let’s examine how you can further customize your autocompleter.

Using Autocompleter Customization
You’ve already seen that the Ajax.Autocompleter constructor accepts four parameters (the fourth being an optional literal object used to customize the autocompleter). Table 1 shows the most used properties of this object accompanied by short descriptions.

Table 1. Ajax-based Autocompleter Customization Options
Option Description
paramName This property is the name to use for the parameter sent to the server. It defaults to the text box name and its value is represented by the string typed by the user.
frequency This property is the time?since the last character inserted by the user?to wait before sending the request to the server. It defaults to 0.4 seconds.
minChars This property is the number of characters after which the search starts. It defaults to 1.
parameters This property is for further parameters to send to the server. They must have a query string format (e.g., param1=value1&param2=value2…).
indicator This property is the ID of an HTML element to display during the data fetching. Generally it is a GIF that indicates a data loading.
updateElement This property is the callback function to call when the user chooses an option from the list. The default function inserts the item chosen in the text box.
afterUpdateElement This property is the callback function to invoke after updateElement is called. No function is defined by default.

The following code example uses some of the options in Table 1. It creates an autocompleter that sends the request to the server after the user inserts at least two characters and after half a second has passed since the user last typed a character:

new Ajax.Autocompleter(     'autoCompleteTextField',     'autoCompleteMenu',     'countries-list.php',     {          minChars: 2,          frequency : 0.5,          indicator : "loadingGif",          afterUpdateElement : function(){alert($F("autoCompleteTextField"));},     });

Furthermore, the above code displays the HTML element with loadingGif as the ID and shows an alert box containing the chosen item after the user chooses it from the list. Try using this code with the ajax-autocomplete-options.htm file in the code download for this article.

You can use the options in Table 1 in the local autocompleter as well?even if some of them, such as parameters, don’t make sense for the local counterpart.

Using Local Autocompleter
Besides the Ajax-based autocompleter, Script.aculo.us lets you use a local autocompleter as well. But what does local autocompleter mean? Basically, it is an autocompleter whose data are stored locally within a JavaScript array instead of in a server. The following example shows how to build a local autocompleter:

new Autocompleter.Local(     'autoCompleteTextField',     'autoCompleteMenu',     COUNTRY_LIST,     {          minChars: 2     });

As you can see, it is very similar to the Ajax-based autocompleter except for the third parameter, which is a JavaScript array instead of a server-side component URL. (You can find a fully functional example in the local-autocomplete.htm file of the downloadable code.) Table 2 shows the parameters you can use to customize a local autocompleter?besides the ones already shown in Table 1.

Table 2. Local Autocompleter Customization Options
Option Description
choices This property is the maximum number of items to display within the list. It defaults to 10.
partialChars This property is related to partialSearch and is the number of characters to type before even trying a partial match.
partialSearch This property is a boolean value that indicates if the partial search must be enabled. It true, the beginning of each word is checked against the match. It is true by default.
fullSearch This property is a boolean value that, if true, checks for a match not only at the beginning of the word but also within it. For example, if you search mil, then both Milan and Hamilton will match.
ignoreCase This property is a boolean value that indicates whether the search must be case-insensitive. It’s true by default.

Implementing a Custom Autocompleter
As previously discussed, the problem with the Ajax-based autocompleter is that it expects an unordered list (

    ) as a server response. The scenario in this section demonstrates how to implement an autocompleter that lets you handle any type of response. In this scenario, I intercept the server response?before passing it to the Script.aculo.us function that updates the list?and pass it to a callback function defined among the options passed to the autocompleter constructor.

    The client code implements such a callback function in order to parse the response (XML, JSON or whatever) and build a

      block that will be passed to the Script.aculo.us updateChoices function, which will update the list. I called this autocompleter Ajax.Autocompleter.Custom. Here is its implementation:

      Ajax.Autocompleter.Custom = Class.create(Ajax.Autocompleter, {  initialize: function(element, update, url, options) {    this.baseInitialize(element, update, options);    this.options.asynchronous  = true;    this.options.onComplete    = this.onComplete.bind(this);    this.options.defaultParams = this.options.parameters || null;    this.url                   = url;    this.options.responseProcessor = this.options.responseProcessor || Prototype.K;  },  onComplete: function(request) {       var htmlUL = this.options.responseProcessor(request.responseText, this.element);     if(htmlUL)     {       this.updateChoices(htmlUL);     }  }});

      The only part I changed with respect to the default Ajax.Autocompleter implementation is the definition of a further parameter, responseProcessor, and the redefinition of onComplete. The responseProcessor parameter must be a callback function that expects two parameters:

      • The server response
      • The element to which the autocompleter is associated (You won’t use this parameter very often in your callback functions.)

      I redefined onComplete to intercept the call to updateChoices so that the callback function defined by the client code can process the server response and prepare the correct string expected by updateChoices. Listing 3 provides an example that uses this brand-new autocompleter (you can find the full code in the source code archive for this article).

      As you can see, using the custom autocompleter is pretty simple. You create an instance of its class and pass a callback function among its parameters. In the previous example, this function is jsonResponseProcessor and it handles the JSON response format. You can easily implement a callback function that parses an XML response in much the same way. It’s up to you to define the correct response processor, provided that the output of your callback function is always a string representing a

        block.

        Using In-Place Editors
        An in-place editor basically is a

        containing text that a user can edit by clicking on it. The actors involved in such a control are:

        • An instance of the Ajax.InPlaceEditor class
        • A server-side component that receives the edited text and processes it (stores it in a database, sends it through e-mail, and so on)

        Listing 4 provides an example that creates a very simple in-place editor.

        The server component, namely editor-test.php, just takes the edited text and echoes it to the client again. Figure 3 and Figure 4 show the initial state of the editor and how it looks after the user clicks on the text, respectively.


        Figure 3. In-place Editor Initial State: Here is the initial state of the editor.
         
        Figure 4. In-place Editor State, After the User Clicks on Its Text: Here is how the editor looks after the user clicks on the text.

        As the above code shows, creating an in-place editor is a piece of cake using Script.aculo.us. You just need to build an instance of Ajax.InPlaceEditor and pass it the following three parameters:

        • The
          ID
        • The URL of the server component that processes the text
        • The ubiquitous literal object containing the options for customizing the editor

        Table 3 shows the most used options for the in-place editor.

        Table 3. In-place Editor Customization Options
        Option Description
        okText This property is the text to use for the OK button.
        cancelText This property is the text to use for the Cancel link.
        savingText This property is the text displayed while the saving action is in progress. It defaults to “Saving…”.
        rows This property is the number of rows to use for the text field. If greater than 1, it is rendered as a text area. It defaults to 1.
        cols This property is the number of columns to use.
        size This property is the same as cols, but it applies when rows is 1.
        onComplete This property is the callback function to call in case of a successful update.
        onFailure This property is the callback function to call in case of failure.

        Using Sliders
        Sliders are controls you use everyday in your desktop applications. For example, the volume control is a typical example of slider. Basically, a slider consists of a track and a handle you can move along the track. The following example builds a simple slider that displays its value as the user drags its handle:

        Event.observe(window, "load", function()      {          new Control.Slider(               'sliderHandle',               'sliderTrack',               {                    range: $R(0, 100),                    values : [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],                    onChange : updateValue,                    onSlide : updateValue               }          );     });

        The above code is just a snippet of the whole HTML page. You can find it in the downloadable code for this article under the name simple-slider.htm.

        As you can see, you create an instance of Control.Slider and pass the following parameters to it:

        • The
          ID of the handle
        • The
          ID of the track
        • The literal object that lets you customize the slider

        The options used in the previous example are:

        • range: the range of values
        • values: the only values the slider may assume (By defining such an option, the slider may assume only discrete values.)
        • onChange: the callback function to call when the user changes the position of the handle
        • onSlide: the callback function to invoke when the user drags the handle

        In the previous example, I used updateValue as the callback function for both onChange and onSlide. This function just updates the state of the

        that displays the slider’s current value.

        The only requirement for the HTML code is that the handle must be a child element of the track, as follows:

        Figure 5 shows the slider in action.

        Figure 5. Slider in Action: Here is the slider in action.

        Of course, you can manipulate the CSS code to further customize your slider appearance. For example, you might want to use images for both the track and the handle to make the slider more attractive.

        One last tip: You can change the slider value programmatically as the following example shows:

        var sliderRef = new Control.Slider(          'sliderHandle',          'sliderTrack',          {               range: $R(0, 100),               values : [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],               onChange : upDateValue,               onSlide : updateValue          }     );// set the slider valuesliderRef.setValue(80);

        First, you need to attach the slider instance to a reference and then you may change its value by using the setValue method of the Control.Slider class.

        Improving the End-User Experience
        In the world of application development, desktop applications used to be preferred to their web counterparts for many reasons, such as responsiveness and the available set of controls. Nowadays, however, leveraging Ajax and UI libraries such as Script.aculo.us, developers can develop web applications that are both responsive and graphically appealing. This article showed you how to integrate Script.aculo.us controls into your web applications to make the end-user experience more pleasant.

        Share the Post:
        Share on facebook
        Share on twitter
        Share on linkedin

Overview

Recent Articles: