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.
These are not complete references. A complete set of references for these frameworks would occupy a small bookshelf.
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/.
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
.
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.
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 on
XXXX
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 on
XXXX
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.
The onUninitialize
,
onLoading
, onLoaded
, and onInteractive
callbacks are not
consistently implemented by all browsers. In general, it is better
to avoid using these callbacks.
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¶meter2=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
.
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.
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.
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.
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.
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.
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.
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 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/.
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
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 |
---|---|
| The name of the parameter for the string typed by the user on the auto-complete field. |
| Tokens from the
|
| The frequency between checks to the server. |
| The minimum number of characters required in the field before making requests to the server. |
| The indicator to
show when the |
| 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. |
| 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.
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 |
---|---|
| How many auto-completion choices to offer. |
| 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 |
| Indicates whether
to search anywhere in the auto-complete array strings. The
default value is |
| How many characters
to enter before triggering a partial match (unlike
|
| Indicates whether
to ignore case when auto-completing. The default is
|
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>
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.
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
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 |
---|---|---|
| The |
|
| 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. |
|
| This determines
whether a Cancel link will be shown in edit mode. Possible
values are |
|
| The text of the link that cancels editing. |
|
| The text shown
during |
|
| The number of columns the text area should span. This works for both single-line and multiline text editing. | None |
| The |
|
| The CSS class used for the in-place edit form. |
|
| The |
|
|
| |
| The color to which the highlight will fade. |
|
| The CSS class that is used when the form is hovered over with the mouse. | |
| When the |
|
| This will cause the text to be loaded from the URL on the server (useful if the text is actually formatted on the server). |
|
| This determines
whether a Submit button will be shone in edit mode.
Possible values are |
|
| The text of the Submit button that submits the changed value to the server. |
|
| The code to run if the update with the server is successful. |
|
| The code to run if the update with the server fails. |
|
| The row height of
the input field (anything greater than |
|
| The CSS class added
to the element while displaying the |
|
| The text shown while the text is sent to the server. |
|
| This is a synonym
for | None |
| Determines whether
the |
|
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 POST
ed to the server), and should send
the new value as the body of the response.
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( );
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.
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 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 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 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.
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.
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/.
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/.
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.
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.
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?
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/.
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 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.
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?
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.
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.
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 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 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 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!
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 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/.
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.
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 |
---|---|
| This is thrown by a
|
| This is thrown when the JavaScript runtime is not capable of performing the given function. |
| This is thrown by a
|
| The results passed to
|
| This is thrown when
an |
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 |
---|---|
| This method adds the
same function as both a |
| This method adds a
single |
| This method adds a
separate |
| The method adds a
single |
| The method begins the
callback sequence with a non- |
| This method cancels a
|
| 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 |
---|---|
| This function calls
the passed |
| This function
performs a customized The following options are available:
|
| This function
performs a simple |
| This function
evaluates a JSON |
| This function returns
a |
| This function is a
convenience function that returns a |
| This function returns
an |
| This function calls a
|
| This function does a
simple |
| This function sets an
|
| This function returns
a |
| This function returns
a new cancelable |
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) { ... }
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 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/.
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 |
---|---|
| This function allows you to attach a function to be executed whenever an Ajax request completes. |
| This function allows you to attach a function to be executed whenever an Ajax request fails. |
| This function allows you to attach a function to be executed before an Ajax request is sent. |
| This function allows you to attach a function to be executed whenever an Ajax request begins and none is already active. |
| This function allows you to attach a function to be executed whenever an Ajax request has ended. |
| This function allows you to attach a function to be executed whenever an Ajax request completes successfully. |
| This function loads a remote page using an HTTP request. |
| This function allows for the setup of global settings for Ajax requests. |
| This function sets the timeout for all Ajax requests to a specified amount of time. |
| This function loads a
remote page using an HTTP |
| This function loads a
remote page using an HTTP |
| This function loads
JSON data using an HTTP |
| This function loads
and executes a local JavaScript file using an HTTP |
| This function loads a
remote page using an HTTP |
| This function loads HTML from a remote file and injects it into the DOM. |
| This function loads HTML from a remote file and injects it into the DOM, only if the server has not modified it. |
| 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.
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.