Appendix B. JavaScript Framework, Toolkit, and Library References

The many available JavaScript frameworks, toolkits, and libraries make it easier and quicker for developers to create Ajax applications. These code bases give two main things to developers: robust Ajax objects and browser effects objects. This appendix demonstrates the Ajax capabilities of some of the most popular frameworks, toolkits, and libraries available. It also briefly discusses the effects that these JavaScript code bases may have available. What is interesting is how these different frameworks, toolkits, and libraries implement the same basic Ajax technology, and how they enhance and strengthen it with their own ideas.

Tip

These are not complete references. A complete set of references for these frameworks would occupy a small bookshelf.

Prototype Framework Reference

The Prototype Framework allows for the development of dynamic web applications in an object-oriented environment. The standards-compliant code, written and maintained by Sam Stephenson (among others), takes the burden associated with creating these applications away from the developer. The development of this framework is driven largely by the Ruby on Rails framework, though it is usable in any environment.

This reference is based on version 1.5.1. You can find more information and full documentation of the Prototype Framework at http://www.prototypejs.org/.

Ajax with Prototype

The Prototype Framework makes it very easy to deal with Ajax in a way that is both cross-browser-compliant and simple, taking the difficulty out of building applications around calls. Besides simple requests, Prototype’s Ajax module also helps developers deal with the JavaScript code (JavaScript Object Notation [JSON]) returned from a server, and provides helper classes for server polling.

Ajax functionality is contained in the global Ajax object. The transport for Ajax requests is in XMLHttpRequest, with browser differences abstracted from the user in a transparent way. Actual requests are made by creating instances of the Ajax.Request object:

new Ajax.Request('<request URL>', {
    method: 'get'
});

The first parameter is the URL of the request, and the second parameter is the options hash, a set of key/value pairs that affect the output of the object. The method option refers to the HTTP method to be used. The default method is POST.

Tip

It is good to remember that for security reasons (preventing cross-site scripting attacks, etc.), Ajax requests can be made only to URLs of the same protocol, host, and port of the page containing the Ajax request. Some browsers may allow arbitrary URLs, but it is not a good idea to rely on support for this.

Ajax Response Callbacks

By default, Ajax requests are asynchronous, meaning that callbacks are necessary to handle the data that will come back from a response. Callback methods are passed in the options hash when making a request:

new Ajax.Request('<request URL>', {
    method: 'get',
    onSuccess: function(p_xhrResponse) {
        var response = p_xhrResponse.responseText || 'NO RESPONSE TEXT';

        alert('Success! 

' + response);
    },
    onFailure: function( ) {
        alert('Something went wrong.'),
    }
});

Two callback methods are passed in the hash of the preceding example, alerting us of either success or failure of the Ajax request: onSuccess and onFailure are called accordingly based on the status of the response. The first parameter that is passed to both callback methods is the native XMLHttpRequest object from which you can use its responseText and responseXML properties, respectively. Note that the second callback method is not expecting the XMLHttpRequest object, and does not provide a parameter for it.

Both, one, or neither callback may be specified in the options hash; that is entirely up to the developer. Other available callbacks that may be specified are:

  • onUninitialized

  • onLoading

  • onLoaded

  • onInteractive

  • onComplete

  • onException

Each callback matches a certain state of the XMLHttpRequest transport, except for onException, which is executed when there is an exception while dispatching other callbacks.

In addition to these callbacks, there are also onXXXX callbacks, where the XXXX is the HTTP response status (things such as 200 or 404). You should be aware that if these callbacks are used, the onSuccess and onFailure callbacks will not be executed because the onXXXX callbacks always take precedence. If a developer is going to use these callbacks, he better know what he is doing, and have a specific reason for doing it.

Warning

The onUninitialize, onLoading, onLoaded, and onInteractive callbacks are not consistently implemented by all browsers. In general, it is better to avoid using these callbacks.

Passing Parameters to the HTTP Method

Making simple calls to the server is fine, but Ajax is more useful when parameters can also be sent with the call. To send parameters with Prototype’s Ajax object, pass the parameters property as part of the options hash:

new Ajax.Request('<request URL>', {
    method: 'get',
    parameters: { param1: 'value1', param2: value2 }
});

Parameters can be passed with this property in two ways: as a hash (the preferred method) or as a string of key/value pairs separated by ampersands (e.g., parameter1=value1&parameter2=value2).

Parameters may be used with both GET and POST requests, but you should keep in mind that GET requests to your application should not cause data to be changed. Another thing to consider is that browsers are less likely to cache a response to a POST request, and are more likely to do so with GET.

Tip

Internet Explorer is notorious for caching the responses to Ajax requests, whether the XMLHttpRequest method is GET or POST. A simple way around this issue is to add an additional parameter to your parameters property that has the current time in nanoseconds. As an ever-changing value, the browser will never be able to cache the request.

One of the primary applications for the parameters property is sending the contents of a <form> element with an Ajax request. Instead of parsing all of the <input> elements in the <form> to build what needs to be sent, Prototype has a helper method available specifically for this, called Form.serialize( ). For example:

new Ajax.Request('<request URL>', {
    method: 'get',
    parameters: $('<form_id>').serialize(true)
});

Other parameters that are sometimes forgotten but may also be sent with the Ajax request are custom HTTP request headers. If you need to send these headers, the requestHeaders option is available. The value of this option is either a hash of name-value pairs or a flattened array of name-value pairs, such as:

['X-Custom-1', 'value', 'X-Custom-2', 'other value']

When you need a custom post body (not parameters in the parameters option) to go along with a POST request, you can use a postBody option. You would use it for content such as this:

<request>
    <parameters>
        <param1>value1</param1>
        <param2>value2</param2>
    </parameters>
</request>

When using the postBody option, parameters that are passed with the parameters option will never be posted because postBody takes precedence as a body for the POST. Using this option means the developer knows what she is doing.

Evaluating JSON

Plenty of times the response from the server is returned not as plain text or XML, but rather as JSON. When the MIME type that the client receives matches the text/javascript type, Prototype will automatically eval( ) the response. There is no need for the developer to handle this case explicitly.

When the response holds an X-JSON header, however, its content will be parsed, saved as an object, and sent to the callbacks as the second argument in the method. For example:

new Ajax.Request('<request URL>', {
    method: 'get',
    parameters: $('<form_id>').serialize(true),
    onSuccess: function(p_xhrResponse, p_jsonResponse) {
        alert(((p_jsonResponse) ? Object.inspect(p_jsonResponse) :
            'NO JSON OBJECT'));
    }
});

This functionality of Prototype is handy for when you want to fetch nontrivial data using Ajax, but you do not want the overhead associated with parsing XML in the response. JSON is faster and lighter than XML.

The Global Responders

Every Ajax request that uses Prototype tells the Ajax.Responders object when it is created. With this object, callbacks can be registered that will be executed on whatever state is specified for every Ajax.Request issued. For example:

Ajax.Responders.register({
    onCreate: function( ) {
        alert('A request has been initialized.'),
    },
    onComplete: function( ) {
        alert('A request completed.'),
    }
});

Once this code is executed, every callback matching an XMLHttpRequest transport state is allowed here, with an addition of onCreate and onComplete. Globally tracking requests in this manner may be useful in a number of ways. They can be logged for debugging purposes, or a global exception handler could be made that informs users of a possible connection problem.

Dynamic Page Updating

Developers often want to make Ajax requests that receive XHTML fragments as the response that update parts of a document. You can accomplish this with Ajax.Request and an onComplete callback method, but Prototype provides an easier way using Ajax.Updater.

For example, let’s assume that the following XHTML is in your document:

<h2>View a list of products</h2>
<div id="productsContainer">(fetching product list...)</div>

The products container is empty (except for a display message to the user), and it needs to be filled with an XHTML fragment returned with an Ajax response. Doing so is as simple as this:

new Ajax.Updater('productsContainer', '<request URL>', {
    method: 'get'
});

That’s all there is to it with Prototype. The arguments for Ajax.Updater are the same as Ajax.Request, with the exception of an additional argument specifying the receiver element of the response. Prototype will magically update the container with the response using the Element.update( ) Prototype method.

Tip

If the XHTML fragment in the response contains inline <script> elements, they will be automatically stripped by default. To override this, you must pass true as the evalScripts option in the options hash to see your scripts being executed.

What happens, however, if an error occurs and the response is an error message instead of an XHTML fragment? Most of the time, you do not want to insert an error message in places where users expect to see normal page content. Fortunately, Prototype provides a convenient solution to this problem. Instead of the id of the container as the first argument of Ajax.Updater, a developer may pass in a hash of two different containers in this form:

{ success: 'productsContainer', failure: 'errorContainer' }

A successful response will have its content placed in the success container, and an unsuccessful response will have the error message written to the failure container. This allows the interface to remain more user-friendly.

Other times you may choose not to overwrite the existing content of a container, instead wanting to insert new content on the top or bottom of the existing content. You can accomplish this using Insertion.Top or Insertion.Bottom passed in the insertion option of the options hash. For example:

new Ajax.Updater('productsContainer', '<request URL>', {
    method: 'get',
    insertion: Insertion.Top
});

Ajax.Updater will insert the returned XHTML fragment into the productsContainer element. Ajax.Updater is a powerful tool provided by Prototype to complete tasks often needed by developers. This simplifies the developer’s role by reducing the amount of code he needs to write and maintain.

Automating Requests

As great as Ajax.Updater is at simplifying an Ajax request, suppose you want to run the request periodically to repeatedly get updated content from the server. Prototype provides a tool to do this, too: Ajax.PeriodicalUpdater. Ajax.PeriodicalUpdater runs Ajax.Updater at regular intervals. For example:

new Ajax.PeriodicalUpdater('productsContainer', '<request URL>', {
    method: 'get',
    insertion: Insertion.Top,
    frequency: 1,
    decay: 2
});

You will notice the two new options added to the options hash in the preceding code: frequency and decay. frequency is the interval in seconds at which the requests are to be made. It is one second in the example, meaning an Ajax request is executed every second (the default frequency is two seconds).

In this case, the frequency of calls may begin to present a burden to the server; it has to process quite a load. This is especially true if the page with the Ajax.PeriodicalUpdater is left open for some time. This is the reason for the decay option. It is the factor by which the frequency is multiplied every time the response body of the request is the same as the previous one. Requests would start at one second, then two seconds, four seconds, eight seconds, and so on. Whenever there is new content in the response body, this will reset and the decay effect will start over. This factor makes sense only for content that does not change rapidly, and your application gets the same content frequently.

Tip

Frequency decay takes the load off the server considerably because it reduces the number of overall requests. Experimenting with this factor while monitoring server load will give you the best results, or you can turn it off completely (it is turned off by default) by passing a value of 1 or simply omitting the option.

script.aculo.us Library Reference

script.aculo.us is an add-on to the Prototype Framework that provides cross-browser and simple tools to make the user interface more dynamic and engaging. Originally programmed by Thomas Fuchs, script.aculo.us is now enhanced and maintained by a community of developers.

This reference is based on version 1.7.0. You can find more information and full documentation of the script.aculo.us library at http://script.aculo.us/.

Auto-Completion

The script.aculo.us Autocompleter controls allow for Google Suggest-like local and server-powered auto-completing text input fields. These two methods for auto-completion come from the following classes:

  • Ajax.Autocompleter

  • Autocompleter.Local

Ajax.Autocompleter

The Ajax.Autocompleter class allows for server-powered auto-completion of text fields. The syntax for this class is:

new Ajax.Autocompleter(<id of text field>, <id of DIV element to populate>, URL,
    options);

Table B-1 gives a list of available options for this class; the options are inherited from Autocompleter.Base.

Table B-1. A list of available options for the Ajax.Autocompleter class

Option

Description

paramName

The name of the parameter for the string typed by the user on the auto-complete field.

Tokens

Tokens from the Autocompleter.Base.

Frequency

The frequency between checks to the server.

minChars

The minimum number of characters required in the field before making requests to the server.

Indicator

The indicator to show when the Autocompleter is sending the Ajax request (an animated GIF or message).

updateElement

The function to be called after the element has been updated. This function is called instead of the built-in function that adds the list item text to the input field.

afterUpdateElement

The function to be called after the element has been updated. This function is called after the built-in function that adds the list item text to the input field.

The following is the XHTML needed for the Ajax.Autocompleter class to work correctly:

<input type="text" id="txtAutocomplete" name="txtAutocompete" value="" />
<div id="txtAutocomplete_choices" class="autocomplete"></div>

The JavaScript code to create this auto-completer would be:

new Ajax.Autocompleter('txtAutocomplete', 'txtAutocomplete_choices',
    '<request URL>', {});

Alternatively, when an indicator is used as part of the auto-completion, the XHTML would look like this:

<input type="text" id="txtAutocomplete" name="txtAutocomplete" value="" />
<span id="txtAutocomplete_indicator" style="display: none;">
    <img src="<path to images>/spinner.gif" alt="Working..." title="Working..." />
</span>
<div id="txtAutocomplete_choices" class="autocomplete"></div>

This text field would require the creation of an Ajax.Autocompleter with options, such as this:

new Ajax.Autocompleter('txtAutocomplete', 'txtAutocomplete_choices',
        '<request URL>', {
    paramName: 'value',
    minChars: 2,
    updateElement: addItemToList,
    indicator: 'txtAutocomplete_indicator'
});

Either way, the server must send back the correct data structure for this to work correctly. The server must return an unordered list of values as an XHTML fragment, similar to the following:

<ul>
    <li>Ajax: The Definitive Guide</li>
    <li>Ajax Design Patterns</li>
</ul>

To create fancier auto-completion lists (such as drop-down lists), you can alter the structure slightly and define the afterUpdateElement. Then all you need to do is to style the results to your liking with some CSS.

Autocompleter.Local

You use the local array when you would prefer to inject an array of auto-completion options into the page, rather than sending out query requests using Ajax. The syntax for this class is:

new Autocompleter.Local(<id of text field>, <id of DIV element to populate>,
    <array of string data>, options);

The constructor for this class takes four parameters: the first two are the id of the text box and the id of the auto-completion menu, the third is an array of strings to use for the auto-complete, and the fourth is the options hash. Table B-2 lists the extra options available with the Autocompleter.Local class.

Table B-2. Extra options available with the Autocompleter.Local class

Option

Description

Choices

How many auto-completion choices to offer.

partialSearch

Indicates whether to match text at the beginning of any word in the strings in the auto-complete array or to match text entered only at the beginning of strings in the auto-complete array. The default value is true.

fullSearch

Indicates whether to search anywhere in the auto-complete array strings. The default value is false.

partialChars

How many characters to enter before triggering a partial match (unlike minChars in Ajax.Autocompleter, which defines how many characters are required to do any match at all). The default value is 2.

ignoreCase

Indicates whether to ignore case when auto-completing. The default is true.

Tip

It is possible to pass in a custom function as the selector option if you want to write your own auto-completion logic, but beware that in doing so the other options will not be applied unless you support them.

The following gives an example of using the Autocompleter.Local class:

<p>
    <label for="90sRockBand">Your favorite rock band from the 90s:</label>
    <input id="90sRockBand" name="90sRockBand" type="text" size="40" value="" />
</p>
<div id="bandList"></div>

<style type="text/javascript">
    //<![CDATA[
    new Autocompleter.Local('90sRockBand', 'bandList',
        [
            'Aerosmith','Bush','Cake','Candlebox','Collective Soul','Creed',
            'Dave Matthews Band','Everclear','Faith No More','Garbage','Green Day',
            'Guns 'n Roses','Korn','Linkin Park','Live', Metallica',
            'Nine Inch Nails','Nirvana','Oasis','Pantera','Pearl Jam','Prodigy',
            'Radiohead','Rage Against the Machine','Red Hot Chili Peppers',
            'Smashing Pumpkins','Soul Asylum','Soundgarden','Stone Temple Pilots',
            'The Offspring','Tool','U2','Weezer','White Zombie'
        ], {
        minChars: 2
    });
    //]]>
</style>

Autocompleter.Base

The Autocompleter.Base class handles all of the auto-completion functionality that is independent of the data source for auto-completion. This includes drawing the auto-completion menu, observing keyboard and mouse events, and so on.

Specific auto-completers need to provide, at the very least, a getUpdatedChoices( ) method that will be invoked every time the text inside the monitored text box changes. This method should get the text for which to provide auto-completion by invoking this.getToken( ), and not by directly accessing this.element.value. This is to allow incremental tokenized auto-completion. The specific auto-completion logic (Ajax, local, etc.) then belongs in the getUpdatedChoices( ) method.

Tokenized incremental auto-completion is enabled automatically when an Autocompleter is instantiated with the tokens option in the options hash. For example:

new Ajax.Autocompleter('id', 'upd', '<request URL>', {
    tokens: ','
});

This example will incrementally auto-complete with a comma as the token. Additionally, the comma (,) in the example may be replaced with a token array—[',', ' ']—which enables auto-completion on multiple tokens. This is most useful when one of the tokens is a newline ( ), as it allows smart auto-completion after line breaks.

Inline Editing

script.aculo.us offers Flickr-style in-place text editing with Ajax behind the scenes for on-the-fly text boxes. Two classes are available to accomplish this:

  • Ajax.InPlaceEditor

  • Ajax.InPlaceCollectionEditor

Ajax.InPlaceEditor

The Ajax.InPlaceEditor( ) method allows for the editing of data on a page using <input> elements of type text, or <textarea> elements when more than one row is needed. The syntax for this class is:

new Ajax.InPlaceEditor(element, url, [options]);

The constructor takes three parameters. The first is the element that should support in-place editing. The second is the URL to which to submit the changed value. The server should respond with the updated value (the server might have post-processed it or validation might have prevented it from changing). The third is an optional hash of options. Table B-3 gives a list of these options.

Table B-3. A list of available options to use with the Ajax.InPlaceEditor( ) class

Option

Description

Default

ajaxOptions

The options specified to all Ajax calls (loading and saving text). These options are passed through to the Prototype Ajax class.

{}

Callback

A function that will be executed just before the request is sent to the server, and should return the parameters to be sent in the URL. This will get two parameters: the entire form and the value of the text control.

function(p_form) {

Form.serialize(p_form);

}

cancelLink

This determines whether a Cancel link will be shown in edit mode. Possible values are true and false.

true

cancelText

The text of the link that cancels editing.

'cancel'

clickToEditText

The text shown during mouseover events on the editable text.

'Click to edit'

Cols

The number of columns the text area should span. This works for both single-line and multiline text editing.

None

externalControl

The id of an element that acts as an external control used to enter data while in edit mode. The external control will be hidden when entering edit mode, and shown again when leaving edit mode.

null

formClassName

The CSS class used for the in-place edit form.

'inplaceeditor-form'

formId

The id given to the element.

id of the element to edit, plus 'InPlaceForm'

Highlightcolor

The highlight color when the inline editing is active.

Ajax.InPlaceEditor.defaultHighlightColor

highlightendcolor

The color to which the highlight will fade.

'#fff'

hoverClassName

The CSS class that is used when the form is hovered over with the mouse.

 

loadingText

When the loadTextURL option is specified, this text is displayed while the text is being loaded from the server.

'Loading...'

loadTextURL

This will cause the text to be loaded from the URL on the server (useful if the text is actually formatted on the server).

null

okButton

This determines whether a Submit button will be shone in edit mode. Possible values are true and false.

true

okText

The text of the Submit button that submits the changed value to the server.

'ok'

onComplete

The code to run if the update with the server is successful.

function(p_transport, p_element) {

new Effect.Highlight(p_element, {

startcolor: this.options.highlightcolor;

});

}

onFailure

The code to run if the update with the server fails.

function(p_transport) {

alert('Error communicating with the server: ' + p_transport.responseText.stripTags( ));

}

rows

The row height of the input field (anything greater than 1 will cause the <input> element to be replaced with a <textarea> element).

1

savingClassName

The CSS class added to the element while displaying the savingText, which will then be removed when the server responds.

'inplaceeditor-saving'

savingText

The text shown while the text is sent to the server.

'Saving...'

size

This is a synonym for cols.

None

submitOnBlur

Determines whether the in_place_edit form will submit when the <input> element loses focus. Possible values are true and false.

false

When the text is sent to the server, the server-side script should expect to get the value as the parameter value (which will be POSTed to the server), and should send the new value as the body of the response.

Tip

The form data is sent to the server encoded in UTF-8, regardless of the page encoding. This is due to the Prototype method Form.serialize( ).

Disabling and enabling the behavior of the Ajax.InPlaceEditor( ) is as simple as setting it to a variable when the new object is created. For example:

var editor = new Ajax.InPlaceEditor(element, url, [options]);
.
. // Code here...
.
editor.dispose( );

Ajax.InPlaceCollectionEditor

The Ajax.InPlaceCollectionEditor( ) method allows for the editing of data on a page when a <select> element is needed for the editing. It otherwise has the same functionality as the Ajax.InPlaceEditor( ) does. The syntax for this class is:

new Ajax.InPlaceEditor(element, url, { collection: [array], [options] });

The constructor takes three parameters. The first is the element that should support in-place editing. The second is the URL to which to submit the changed value. The server should respond with the updated value (the server might have post-processed it or validation might have prevented it from changing). The third is the hash of options, where there is a collection field that holds the array of values to place in the <option> elements within the <select> element, followed by the optional options found in Table B-3.

Effects

Besides the Ajax functionality provided by script.aculo.us, this library also contains effects that can enhance the feel of an application. These effects can give applications more of a Web 2.0 look with their manipulation of elements on the page. More important, however, these effects can draw a user’s attention to the fact that the page is doing something when Ajax is working behind the scenes. For more information on these effects, see the script.aculo.us documentation Wiki at http://wiki.script.aculo.us/scriptaculous/.

Rico Library Reference

Rico is a library built on top of the Prototype Framework that has a collection of widgets that enhance a web application. A set of some of the tools built at Sabre Holdings (http://www.sabre.com/) that was permitted to be released to the community, Rico was written by Darren James, Richard Cowin, and Bill Scott. The current version of Rico is 1.1.2 (though Rico 2.0 beta 2 is out). You can find more information on Rico at http://openrico.org/.

Ajax with Rico

Ajax with Rico is a little different from the Prototype Ajax objects. Two steps are involved in creating Ajax requests with this library. The first step is to register the request by giving it a logical name and a URL to request to, and registering an element that can have its innerHTML replaced by the HTML passed in the Ajax response. For example:

function Body_OnLoad( ) {
    ajaxEngine.registerRequest('<requestName>', '<requestURL>'),
    ajaxEngine.registerAjaxElement('<elementToUpdate>'),
}

This code would need to be called when the page is loaded, and it sets up the logical name of the request and tells where the request is to go. It then specifies an element that can be modified.

The second step is to send the actual request to the server. Assuming that this next example is called with a comma-delimited string of values, it parses the data out to create parameters to be passed with the request:

function RequestData(p_value) {
    var arrValue = p_value.split(','),
    var parameters = '';

    for (var i = 0, il = arrValue.length; i < il; i++)
        parameters += arrValue[0] + '=' + arrValue[1];
    ajaxEngine.sendRequest('<elementToUpdate>', parameters);
}

When the response is returned with this example, it is handled entirely by the Rico Ajax engine, and it replaces the specified element’s innerHTML with the response.

The Response

The Ajax response to a Rico request must be specifically formatted for the Ajax engine to handle it correctly. The Ajax response must have the following structure:

<ajax-response>
    <response type="[responseType]" id="[elementToUse]">
        <!-- Ajax payload -->
    </response>
</ajax-response>

Rico currently recognizes two response types: element and object. When the response type is element, it is telling the Ajax engine that there should be an element in the page with the passed id, and the payload inside the response should replace the existing innerHTML of the element.

Handling Responses

However, when the response type of the Rico response is object, it is telling the Ajax engine that there is an object with the id that has an ajaxUpdate( ) method to call. The engine calls this method, passing the payload of the response as a parameter. This can then be processed by the page. For example:

<ajax-response>
    <response type="object" id="elementUpdater">
        <!-- Ajax payload -->
    </response>
</ajax-response>

Here, the XML within the <response> element will be processed by the method contained in the object with id elementUpdater. This is more limiting than some libraries, in that XML is expected to be sent back with every request, but it is simple.

Effects

Rico was built for effects, and not Ajax, when it was authored, and it therefore contains some very useful effects tools and behaviors for enhancing web applications. LiveGrid is, of course, Rico’s most popular widget, but it also includes many other robust and simple-to-use effects. Accordions, drag and drop, and rounded corners are just some of the other useful functionalities contained within the Rico library. For more information on the effects and their uses, see the Demos page at http://demos.openrico.org/.

MooTools Library Reference

Valerio Proietti took the JavaScript library moo.fx and turned it into MooTools, a stronger and more functional JavaScript framework. MooTools is lightweight like its predecessor, and features a Prototype-like design that is very modular. The current version of MooTools is 1.11. You can find more information on MooTools at http://mootools.net/.

Simple Server Requests

The XHR class, first introduced in MooTools 1.0, is a basic wrapper for the XMLHttpRequest object. It allows for a simple server request for data with options to control the basics of Ajax requests. Here is the XHR class:

var XHR = new Class({
    Implements: [Chain, Events, Options],
    options: {
        method: 'post',
        async: true,
        data: null,
        urlEncoded: true,
        encoding: 'utf-8',
        autoCancel: false,
        headers: {},
        isSuccess: null
    },
    setTransport: function( ) {
        // setTransport code
    },
    initialize: function( ) {
        // initialize code
    },
    onStateChange: function( ) {
        // onStateChange code
    },
    isSuccess: function( ) {
        // isSuccess code
    },
    onSuccess: function( ) {
        // onSuccess code
    },
    onFailure: function( ) {
        // onFailure code
    },
    setHeader: function(name, value) {
        // setHeader code
    },
    getHeader: function(name) {
        // getHeader code
    },
    send: function(url, data) {
        // send code
    },
    request: function(data) {
        // request code
    },
    cancel: function( ) {
        // cancel code
    }
});

The XHR class is easy to use. For example:

new XHR({ method: 'get', onSuccess: NextFunction}).send('<requestURL>',
    'param1=value1'),

Here, the location of the Ajax request is <requestURL>, sending it a parameter param1 with a value of value1. With a successful request, the function NextFunction( ) is called.

Making an Ajax Request

Ajax is simple to use with MooTools, and it has a similar feel to Prototype’s Ajax classes. MooTools uses the Ajax class to make requests to the server. For example:

new ajax('<requestURL>', {
    postBody: 'param1=value1',
    onComplete: parseResponse,
    update: '<divContainer>'
}).request( );

In this example, an Ajax request is made to <requestURL>, sending it a parameter param1 with a value of value1. The text results are placed into the update container <divContainer>, and the function parseResponse( ) is called when the request is complete with the parameter request (the XHR response).

Looking at the Ajax class will give you a better idea of the object and what you can set with it:

var Ajax = XHR.extend({
    options: {
        data: null,
        update: null,
        onComplete: Class.empty,
        evalScripts: false,
        evalResponse: false
    },
    initialize: function(url, options) {
        // initialize code
    },
    onComplete: function( ) {
        // onComplete code
    },
    request: function(data) {
        // request code
    },
    evalScripts: function( ) {
        // evalScripts code
    },
    getHeader: function(name) {
        // getHeader code
    }
});

As you can see, the Ajax class is built as an extension of the XHR class, which holds the meat of the code for making requests to the server. As such, you can set all of the XHR options here as well. Think of the Ajax class as the robust class for making requests to the server.

Form Submission

The Ajax class is built with the ability to easily send a form as an Ajax request through a special extension, shown here:

Object.toQueryString = function(source) {
    var queryString = [];
    for (car property in source) queryString.push(encodeURIComponent(property)
+ '=' encodeURIComponent(source[property]));
    return queryString.join('&'),
};

Element.extend({
    send: function(options) {
        return new Ajax(this.getProperty('action'),
            $merge({data: this.toQueryString( )}, options,
            {method: 'post'})).request( );
    }
});

Now, when you have a basic form that needs to be submitted, it is as simple as:

<form id="myForm" action="submit.php method="post">
    <input type="text" id="username" name="username" value="" />
    <input type="password" id="passwd" name="passwd" value="" />
    <input type="button" value="Login" onclick="$('myForm').send( );" />
</form>

It doesn’t get much easier than that, does it?

Effects

MooTools does much more than provide a wrapper for Ajax requests to the server. It also provides a number of effects that can enhance your application and give it more of a Web 2.0 look. These effects range from drag-and-drop functionality to element effects that provide transitions and other sliding and morphing effects that can highlight Ajax responses to the user. For a full list of the latest effects and documentation on how to use them, consult the MooTools web site at http://docs.mootools.net/.

Dojo Toolkit Reference

The Dojo Toolkit is a JavaScript library full of widgets and packages that allow you to build a version of the toolkit that meets the needs of your application. It is built off several earlier libraries such as f(m), Burstlib, and nWidgets. Alex Russell began talking with authors of other libraries, and together they formed what is now Dojo. The Dojo Foundation (formed in 2005) maintains the code, which is currently at version 0.9. For more information on the Dojo Toolkit, visit http://dojotoolkit.org/.

dojo.io.bind

Dojo allows you to bind all Ajax requests, instead of calling the request object directly, giving a little more error handling than you would normally get. You can find this IO method in /src/io/common.js, and it looks like this:

dojo.io.bind = function (request) {
    if (!(request instanceof dojo.io.Request)) {
        try {
            request = new dojo.io.Request(request);
        }
        catch (e) {
            dojo.debug(e);
        }
    }
    // .
    // .
    // .

Sending an Ajax request is easy with dojo.io.bind( ), and it has a simple format to follow:

dojo.io.bind ({
    url: '<requestURL>',
    load: function(type, data, event) {
        // functionality
    },
    error: function(type, data, event) {
        // functionality
    },
    timeout: function(type, data, event) {
        // functionality
    },
    timeoutSeconds: <value>,
    mimetype: 'text/html'
});

Requesting data with Ajax is easy with the Dojo Toolkit, but what about the data that is sent back with a server response? Dojo has different methods for accessing elements from within JavaScript than the other libraries and frameworks we have looked at thus far in this appendix.

Handling Results

With Dojo, an element is accessed directly with its id attribute through the dojo.byId( ) method. Say you have an element meant to hold the data from an Ajax server response, like this:

<div id="container"></div>

It’s easy to access this element’s innerHTML with Dojo by using the following:

dojo.byId('container').innerHTML

Thinking about our Ajax example from before, we want to take the data response from the server and place that information into the innerHTML of our container <div> element. Everything needed to configure an Ajax request and handle the corresponding response can be placed in an object, and Dojo can then bind that object. For example:

var binding = {
    url: '<requestURL>',
    mimetype: 'text/html',
    load: function(type, data, event) {
        dojo.byId('container').innerHTML = data;
    }
};

dojo.io.bind(binding);

Here, the URL, MIME type, and callback functions are handled in our binding object. You will also see that the data from the response is set to the innerHTML of our container element. Handling data is pretty simple with Dojo when it is normal data coming back, but what about JSON information?

JSON and Dot Notation

It’s easy to handle JSON with dot notation that Dojo uses when dealing with JSON objects, which makes handling data sent back from a server simple to manipulate and parse when it is sent back as JSON. Take this JSON data, for example:

{'AjaxBooks':
    [
        {'book':
            { 'id': '1' },
            'title': 'Ajax: The Definitive Guide',
            'isbn': '0-596-52838-8'
        },
        {'book':
            { 'id': '2' },
            'title': 'Ajax Hacks',
            'isbn': '0-596-10169-4'
        },
        {'book':
            { 'id': '3' },
            'title': 'Securing Ajax Applications',
            'isbn': '0-596-52931-7'
        }
    ]
}

Here, we have some simple book data that is sent back to the client from the server, formatted as JSON text. You can manipulate this data with dot notation as follows:

JSON.AjaxBooks[0].book.id;    // 1
JSON.AjaxBooks[2].title;    // Securing Ajax Applications

Using this parsing method to manipulate the contents of a page, in this next example, you can see Dojo making an Ajax request that will change the values of certain elements when the data is returned:

var binding = {
    url: '<requestURL>',
    method: 'post',
    load: function(type, data, event) {
        var json = eval('(' + data + ')'),

        dojo.byId('title').innerHTML = json.AjaxBooks[0].title;
        dojo.byId('isbn').innerHTML = json.AjaxBooks[0].isbn;
    }
};

dojo.io.bind(binding);

Dojo makes Ajax requests pretty simple, as it does with most things in its toolkit. Using dojo.io.bind( ) with an object built for the request keeps everything together and allows for easy additional Ajax requests if needed.

Sending Form Data

Dojo gives you the flexibility to encode parameters in the URL, but it also has a nice feature for sending the contents of an entire form by using the formNode parameter. For example:

var binding = {
    url: '<requestURL>',
    method: 'post',
    load: function(type, data, event) {
        // functionality
    },
    error: function(type, data, event) {
        // functionality
    },
    timeoutSeconds: <value>,
    timeout: function(type, data, event) {
        // functionality
    },
    formNode: dojo.byId('myForm')
};

dojo.io.bind(binding);

This allows for easier data submission of a form with Ajax instead of relying on the traditional full-page posting of the classical Web.

The Rest of Dojo

There is a lot more to Dojo than Ajax, so much more that it would take a whole other book to include it all. Dojo, being package-based, has many developers working on individual pieces of the toolkit at any one time, and this architecture has had great success with the number and variety of packages that have been produced, from Web 2.0 effects and enhancements to helpful charting tools and other application-type widgets. You can find more information on the rest of Dojo at http://dojotoolkit.org/docs.

Sarissa Library Reference

Sarissa is a JavaScript library that prides itself on being compliant with ECMAScript, and it is a cross-browser wrapper for XML Document Object Model (DOM) manipulation. It is lightweight and focuses on the cross-browser problems that plague developers. The current version of Sarissa is 0.9.8.1. For more information on Sarissa, visit http://dev.abiss.gr/sarissa/.

Sarissa’s Ajax Request

Sarissa takes a more traditional approach to Ajax requests and DOM manipulation as a whole. Because of this, Ajax requests and their responses will have the same basic look and feel as the standard way of making Ajax calls. However, Sarissa uses a wrapper to execute all the functionality Ajax needs.

Everything with Sarissa starts with the DOM document, and Ajax is no exception. The first thing you must do before making Ajax requests with Sarissa is to create a document—for example, var oDomDoc = Sarissa.getDomDocument( );. The request and callback function handler are defined from there. Here is a typical example:

var oDomDoc = Sarissa.getDomDocument( );

oDomDoc.async = true;
oDomDoc.onreadystatechange = handleChanges;
oDomDoc.load('<requestURL>'),

function handleChanges( ) {
    if (oDomDoc.readyState == 4) {
        // functionality
    }
}

With Sarissa, even sending the request asynchronously is not a given, and you must define it as part of the setup. You need to create a state change handler if the server will receive any data, and then make the call with any parameters that need to be passed with the request. As I said, this process generally reflects how a standard Ajax call would be made.

Parsing Data

Parsing data returned from the server in response to an Ajax request is also handled in much the same way as it would when the data passed is XML. Any XML DOM manipulation you would have done to a normal responseXML object, you can do directly on the oDomDoc, which is the returned data. For example:

function parseData( ) {
    var myData = oDomDoc.getElementsByTagName('data1'),

    for (var i = 0, il = myData.childNodes.length; i < il; i++) {
        var data = myData.childNodes[i];

        $('container').innerHTML = data;
    }
    $('container').addClassName(oDomDoc.getElementsByTagName('class')[0]);
}

For more information on what Sarissa can do as far as manipulation with JavaScript and the DOM, refer to Chapter 5 of this book.

Sarissa does offer tools that go above what the DOM natively offers in the browser. A handy function is Sarissa’s ability to serialize the returned XML. For example:

function parseData( ) {
    var serialized = Sarissa.serialize(oDomDoc);

    $('container').innerHTML = serialized;
}

This makes the XML much more human-readable, in cases where this may be necessary. It certainly makes it easier to debug returned data!

Sarissa and XML

As you no doubt have seen, Sarissa is built to handle XML and wrap all the XML functionality of JavaScript to make it simpler for you to develop with. It specializes in everything to do with manipulating XML and gives the tools to do so: XPath, XSLT, and XML functionality are all a part of the Sarissa library. For this reason, when all you need for an application is a lightweight library to handle Ajax and other XML functions, Sarissa is an excellent choice. It is a lesser option when the library requires other functionality, such as the ability to handle JSON. When it comes to XML, however, there is nothing better. For more information on Sarissa’s XML functionality, visit http://dev.abiss.gr/sarissa/jsdoc/index.html.

MochiKit Library Reference

MochiKit is a JavaScript library that simplifies development by taking ideas from Python and other languages and implementing them in JavaScript. Bob Ippolito developed MochiKit in 2005, and it is now maintained and enhanced by a large number of contributors from the community. Having excellent documentation, MochiKit prides itself on its documentation and on its in-depth testing of all code. The current version of MochiKit is 1.3.1. You can find more on MochiKit at http://www.mochikit.com/.

MochiKit.Async

The MochiKit.Async object enables you to manage asynchronous code on the client. This model was inspired by Twisted. For example:

var json = loadJSONDoc(URL);

function ParseData(p_meta) {
    if (MochiKit.Async.VERSION == p_meta.version)
        alert('You have the latest version of MochiKit.Async'),
    else
        alert('MochiKit.Async version' +  + 'is available, please upgrade1'),
}

function FetchError(p_err) {
    alert('There was a problem fetching the meta data for MochiKit.Async.'),
}

json.addCallbacks(parseData, FetchError);

The real Ajax implementation in MochiKit, however, is controlled by a different object in the library.

Ajax in MochiKit

The Deferred object allows for all asynchronous requests that happen only once to be consistent across the implementation on the client. This wraps XHR functionality and adds features that make it a robust object.

Deferred has different error handlers built into it to tackle the different types of problems that could occur with an asynchronous request, making it easier to handle errors in an effective manner. Table B-4 shows these error types and describes what they are for.

Table B-4. The error types available with the Deferred object in MochiKit

Error

Description

AlreadyCalledError

This is thrown by a Deferred object if .callback or .errback is called more than once.

BrowserComplianceError

This is thrown when the JavaScript runtime is not capable of performing the given function.

CancelledError

This is thrown by a Deferred object when it is canceled, unless a canceler is present and throws something else.

GenericError

The results passed to .fail or .errback of a Deferred object are wrapped by this error if !(result instanceof Error).

XMLHttpRequestError

This is thrown when an XMLHttpRequest does not complete successfully for any reason.

A Deferred object creates the functionality that will surround an Ajax request, using constructor methods it contains. For example:

var deferred = new Deferred( );

deferred.addCallback(myCallback);
deferred.addErrback(myErrback);

Table B-5 lists the methods the Deferred object uses to create an instance of the object.

Table B-5. The methods used by the Deferred object to create an instance of the object

Method

Description

addBoth(func)

This method adds the same function as both a callback and an errback as the next element on the callback sequence.

addCallback(func[,...])

This method adds a single callback to the end of the callback sequence.

addCallbacks(callback, errback)

This method adds a separate callback and errback to the end of the callback sequence.

addErrback(func)

The method adds a single errback to the end of the callback sequence.

callback([result])

The method begins the callback sequence with a non-Error result.

cancel( )

This method cancels a Deferred that has not yet received a value, or is waiting on another Deferred as its value.

errback([result])

This method begins the callback sequence with an error result.

The Deferred object has three states associated with it: -1 means No value yet, 0 means Success, and 1 means Error.

These values define the state of the Deferred object, and not the actual Ajax request or response.

Other functions are used with Deferred objects that handle the bulk of the work involved in creating asynchronous code in an application, as shown in Table B-6.

Table B-6. Functions associated with Deferred objects

Function

Description

callLater(seconds, func[, args...])

This function calls the passed func after at least seconds seconds have elapsed, and returns a cancelable Deferred object.

doXHR(url[, {options}])

This function performs a customized XMLHttpRequest and wraps it with a Deferred object that may be canceled and returns with a Deferred object that will call back with the XMLHttpRequest instance on success.

The following options are available:

method

The HTTP method, which defaults to GET.

sendContent

The content to send with the request.

queryString

Used to build a query string to append to the url using MochiKit.Base.queryString.

username

The username for the request.

password

The password for the request.

headers

Additional headers to set in the request, either as an Object or as an Array.

mimeType

An override MIME type for the request.

doSimpleXMLHttpRequest(url[, queryArguments...])

This function performs a simple XMLHttpRequest and wraps it with a Deferred object that may be canceled and returns a Deferred object that will call back with the XMLHttpRequest instance on success.

evalJSONRequest(req)

This function evaluates a JSON XMLHttpRequest response and returns the resulting JavaScript object.

fail([result])

This function returns a Deferred object that has already had .errback(result) called.

gatherResults(deferreds)

This function is a convenience function that returns a DeferredList object from the given Array of Deferred object instances that will call back with an Array of just results when they are available, or errback on the first array.

getXMLHttpRequest

This function returns an XMLHttpRequest-compliant object for the current platform.

maybeDeferred(func[, argument...])

This function calls a func with the given arguments and ensures that the result is a Deferred object.

loadJSONDoc(url[, queryArguments...])

This function does a simple XMLHttpRequest to a url and gets the response as a JSON document.

sendXMLHttpRequest(req[, sendContent])

This function sets an onreadystate handler on an XMLHttpRequest object and sends it off, and returns a Deferred object that will call back with the XMLHttpRequest instance on success.

succeed([result])

This function returns a Deferred object that has already had .callback(result) called.

wait(seconds[, res])

This function returns a new cancelable Deferred object that will .callback(res) after at least seconds seconds have elapsed.

An example Ajax request with MochiKit using these functions would look something like this:

var deferred = doXHR('<requestURL>', {
    method: 'POST',
    sendContent: 'data1=value1&data2=value2'
});

deferred.addCallbacks(parseData, handleError);

function parseData(res) { ... }

function handleError(res) { ... }

The Rest of MochiKit

MochiKit provides much more functionality than the ability to control asynchronous threads in JavaScript using a deferred object methodology. MochiKit also offers objects for logging in an application, and Web 2.0 functionality such as drag and drop and sorting. You can find more information on what MochiKit provides at http://www.mochikit.com/doc/html/MochiKit/index.html.

jQuery Library Reference

jQuery was developed as a means of creating better JavaScript syntax to use CSS selectors than any existing library in 2005. Created by John Resig and now maintained and developed by the jQuery team (composed of community volunteers), jQuery has a syntax similar to the Prototype Framework. The current version of jQuery is 1.1.4. You can find more information about jQuery at http://jquery.com/.

Ajax with jQuery

You can make Ajax calls with jQuery in several ways, thanks to several functions available for specific requesting needs. Table B-7 shows these functions.

Table B-7. jQuery functions for providing Ajax request functionality

Function

Description

ajaxComplete(callback)

This function allows you to attach a function to be executed whenever an Ajax request completes.

ajaxError(callback)

This function allows you to attach a function to be executed whenever an Ajax request fails.

ajaxSend(callback)

This function allows you to attach a function to be executed before an Ajax request is sent.

ajaxStart(callback)

This function allows you to attach a function to be executed whenever an Ajax request begins and none is already active.

ajaxStop(callback)

This function allows you to attach a function to be executed whenever an Ajax request has ended.

ajaxSuccess(callback)

This function allows you to attach a function to be executed whenever an Ajax request completes successfully.

jQuery.ajax(options)

This function loads a remote page using an HTTP request.

jQuery.ajaxSetup(options)

This function allows for the setup of global settings for Ajax requests.

jQuery.ajaxTimeout(time)

This function sets the timeout for all Ajax requests to a specified amount of time.

jQuery.get(url, data, callback)

This function loads a remote page using an HTTP GET request.

jQuery.getModified(url, data, callback)

This function loads a remote page using an HTTP GET request, only if it has not been modified since it was last retrieved.

jQuery.getJSON(url, data, callback)

This function loads JSON data using an HTTP GET request.

jQuery.getScript(url, callback)

This function loads and executes a local JavaScript file using an HTTP GET request.

jQuery.post(url, data, callback)

This function loads a remote page using an HTTP POST request.

load(url, data, callback)

This function loads HTML from a remote file and injects it into the DOM.

loadModified(url, data, callback)

This function loads HTML from a remote file and injects it into the DOM, only if the server has not modified it.

serialize( )

This function serializes a set of input elements into a string of data.

The jQuery.ajax(method) will give you the necessary functionality to make the most flexible type of request. For example:

$.ajax({
    url: '<requestURL>',
    type: 'POST',
    data: 'data1=value1&data2=value2',
    success: parseData
});

function parseData(resp) { ... }

If you know you do not need the flexibility of jQuery.ajax( ), use one of the other functions that jQuery provides. This makes it easier on the developer by configuring parameters automatically, instead of dynamically when a request is created.

Other jQuery Functionality

jQuery offers other functionality, as do most other libraries in this appendix. Beyond Ajax wrappers and their associated functionalities, jQuery will give you the special effects and functionality to create Web 2.0 applications. More than that, though, jQuery provides good tools for bringing more powerful CSS into the JavaScript of a web page. For more information on jQuery’s additional functionality, see http://docs.jquery.com/Main_Page.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset