Web developers work with a number of data formats and protocols in transferring information between browsers and servers. This chapter provides a number of recipes for handling and working with some of the most common data formats, Ajax techniques, and jQuery.
You want to make a request to the server for some additional data without leaving the page the visitor is currently on.
Here’s a simple Ajax request:
(function($) { $(document).ready(function() { $('#update').click(function() { $.ajax({ type: 'GET', url: 'hello-ajax.html', dataType: 'html', success: function(html, textStatus) { $('body').append(html); }, error: function(xhr, textStatus, errorThrown) { alert('An error occurred! ' + ( errorThrown ? errorThrown : xhr.status ); } }); }); }); })(jQuery);
At the core of jQuery’s Ajax architecture is the jQuery.ajax()
method. This provides the basis of all browsers to server
requests and responses. So, let’s look at this in a little more
detail. To initiate a request to the server, a settings object that
contains parameters for the request is passed to the $.ajax
method. A vast number of options are
available, with the most common options of a request being type
, url
, complete
, dataType
, error
, and success
:
var options = { type: 'GET' };
The first option that needs to be addressed when starting an Ajax request is the type of HTTP request you’re going to make to the server, which in the majority of cases will be either a GET or POST type:
var options = { type: 'GET', url: 'hello-ajax.html', dataType: 'html' };
Next we’ll look at the URL
and dataType
options. URL
is fairly
self-explanatory with the following interactions worth noting. When
setting the cache
option to
false
, jQuery will append a get
variable of
(for
example _=
<random number>/server-ajax-gateway?_=6273551235126
), which
is used to prevent the browser, proxies, and servers from sending a
cached response. Finally, the dataType
option specifies the data format
that is the expected response from the server. For example, if you’re
expecting the server to return XML, then a value of xml
would be appropriate:
var options = { type: 'GET', url: 'hello-ajax.html', dataType: 'html', error: function(xhr, textStatus, errorThrown) { alert('An error occurred! ' + errorThrown); }, success: function(data, textStatus) { $('body').append( data ); } };
The next two options that we define are two callback methods,
one called error
and the other called success
. They function as they are appropriately titled, with
error
being called when there is an
error with the request and success
being called with a successful response (determined if a server
response type of 200 is returned). The other common option mentioned
is the complete
option, which defines a callback to execute upon after either
success or error of the response:
var options = { type: 'GET', url: 'hello-ajax.html', dataType: 'html', complete: function(xhr, textStatus) { // Code to process response } };
Once the settings have been defined, we can go ahead and execute our request:
var options = { type: 'GET', url: 'hello-ajax.html', dataType: 'html', complete: function(xhr, textStatus) { // Code to process response } }; $.ajax( options );
We can also set our options inline:
$.ajax({ type: 'GET', url: 'hello-ajax.html', dataType: 'html', complete: function(xhr, textStatus) { // Code to process response } });
Our final solution requests the file hello-ajax.html
and appends the contents
(html
) to the <body>
element upon the return of a
successful request. If the request fails, the error
method is triggered instead, alerting the user with a
message:
(function($) { $(document).ready(function() { $('#update').click(function() { $.ajax({ type: 'GET', url: 'hello-ajax.html', dataType: 'html', success: function(html, textStatus) { $('body').append(html); }, error: function(xhr, textStatus, errorThrown) { alert('An error occurred! ' + errorThrown); } }); }); }); })(jQuery);
You have a large web application with Ajax calls occurring throughout the code base and need to define default settings for all requests throughout the application.
(function($) { $(document).ready(function() { $('#loadingIndicator') .bind('ajaxStart', function() { $(this).show(); }) .bind('ajaxComplete', function() { $(this).hide(); }); $.ajaxSetup({ cache: true, dataType: 'json', error: function(xhr, status, error) { alert('An error occurred: ' + error); }, timeout: 60000, // Timeout of 60 seconds type: 'POST', url: 'ajax-gateway.php' }); // Close $.ajaxSetup() }); // Close .read() })(jQuery);
When working with larger applications, often there is a common
Ajax gateway through which all requests are passed. Using the $.ajaxSetup()
method, we can set Ajax request default settings. This would
result in an ease of Ajax requests throughout the application such as
follows:
$.ajax({ data: { // My request data for the server }, success: function(data) { // Now update the user interface } });
A brief side point is that the timeout option takes its value in
milliseconds (seconds×1,000), so a timeout of 6,000
would be six seconds. One thing to
consider when setting this value is the extent to which the Internet
has grown globally. Some of your visitors or users may be in locations
that have a higher latency than you would expect for users within your
region. So, don’t set this value too low (for example five seconds).
Specifying a higher timeout value such as 30 or 60 seconds will allow
users with higher-latency connections (such as those using satellite)
to still enjoy the benefits of your application.
In the previous example, the request will be a POST to ajax-gateway.php
. If an error occurs, it
will be handled by the error function as defined in $.ajaxSetup()
. It is possible to still
override settings for a specific request as follows:
$.ajax({ url: 'another-url.php', data: { // My request data for the server }, success: function(data) { // Now update the user interface } });
The previous request would be sent to another-url.php
instead of ajax-gateway.php
. One beneficial feature of
jQuery’s Ajax architecture is the global events available such as ajaxComplete
, ajaxError
, ajaxSend
, ajaxStart
, ajaxStop
, and ajaxSuccess
. These events may be set up
using the .bind('
event
', callback)
method or the shortcut
.event
(callback)
. The following example shows the
two methods for binding the callback for the ajaxError
event:
(function($) { $(document).ready(function() { $('#loadingIndicator') .ajaxError(function() { // Your code }); // Or using bind() $('#loadingIndicator') .bind('ajaxError', function() { // Your code }); }); })(jQuery);
Here is a rundown and description of the events that are available as well as the order in which they’re triggered:
ajaxStart
Triggered at the start of an Ajax request if no other requests are in progress
ajaxSend
Triggered before each individual request is sent
ajaxSuccess
or ajaxError
Triggered upon a successful or an unsuccessful request
ajaxComplete
Triggered every time a request is complete (regardless of whether it was a success or had an error)
ajaxStop
Triggered if there are no additional Ajax requests in progress
In the next recipe we will build upon these events in more detail.
You need to show a status indicator to the user when Ajax requests are in progress and hide it upon completion.
(function($) { $(document).ready(function() { $('#ajaxStatus') .ajaxStart(function() { $(this).show(); }) .ajaxStop(function() { $(this).hide(); }); // Start our ajax request when doAjaxButton is clicked $('#doAjaxButton').click(function() { $.ajax({ url: 'ajax-gateway.php', data: { val: "Hello world" }, dataType: 'json', success: function(json) { // Data processing code $('body').append( 'Response Value: ' + json.val ); } }); }); }); })(jQuery);
One of the huge benefits of jQuery’s Ajax implementation is the
exposure of global Ajax events that are triggered on all elements with
each Ajax request. In the following solution, we bind two of the
events, ajaxStart
and ajaxStop
using the shortcut methods to the
XHTML element with the ID ajaxStatus
. When the Ajax request is
triggered upon clicking #doAjaxButton
, the ajaxStart
event is also dispatched and calls
show()
on the #ajaxStatus
element. Notice that these
events are triggered automatically and are a by-product of using the
$.ajax()
(or other shortcut methods
such as $.get()
). This provides an
elegant decoupled solution for having an application-wide request
status as Ajax requests are submitted:
(function($) { $(document).ready(function() { $('#ajaxStatus') .ajaxStart(function() { $(this).show(); }) .ajaxStop(function() { $(this).hide(); }); // Start our ajax request when doAjaxButton is clicked $('#doAjaxButton').click(function() { $.ajax({ url: 'ajax-gateway.php', data: { val : 'Hello world' }, dataType: 'json', success: function(json) { // Data processing code $('body').append( 'Response value: ' + json.val ); } }); }); }); })(jQuery);
Let’s look at some of the additional events and the difference
between local and global Ajax events. Local Ajax events (set up using
$.ajaxSetup()
or defined at the
time of $.ajax()
) consist of
beforeSend
, success
, error
, and complete
. These events are defined inline
and tightly coupled to each Ajax request. Global Ajax events are
interleaved with the local events but are triggered for any element
that binds to them and also make use of jQuery’s native event-handling
architecture. Here’s a quick review on how to handle local Ajax events
(such as the complete
event):
$.ajax({ type: 'GET', url: 'ajax-gateway.php', dataType: 'html', complete: function(xhr, textStatus) { // Code to process response } });
Now let’s examine the breakdown, order, and scope in which events are triggered on a successful Ajax request:
ajaxStart
(global)
beforeSend
(local)
ajaxSend
(global)
success
(local)
ajaxSuccess
(global)
complete
(local)
ajaxComplete
(global)
ajaxStop
(global)
For an unsuccessful Ajax request, the order of triggered events
would be as follows with success
and ajaxSuccess
being replaced by
error
and ajaxError
, respectively:
ajaxStart
(global)
beforeSend
(local)
ajaxSend
(global)
error
(local)
ajaxError
(local)
complete
(local)
ajaxComplete
(global)
ajaxStop
(global)
ajaxStart
and ajaxStop
are two special events in the
global scope. They are different in that their behavior operates
across multiple simultaneous requests. ajaxStart
is triggered when a request is
made if no other requests are in progress. ajaxStop
is triggered upon completion of a
request if there are no additional requests in progress. These two
events are only triggered once when multiple simultaneous requests are
dispatched:
(function($) { $(document).ready(function() { $('#ajaxStatus') .ajaxStart(function() { $(this).show(); }) .ajaxStop(function() { $(this).hide(); }); // Start our Ajax request when doAjaxButton is clicked $('#doAjaxButton').click(function() { $.ajax({ url: 'ajax-gateway.php', complete: function() { // Data processing code } }); $.ajax({ url: 'ajax-data.php', complete: function() { // Data-processing code } }); }); }); })(jQuery);
One setting that can be passed into the $.ajax()
method is global
, which can be set to either true
or false
. By setting global
to false
, it’s possible to suppress the global
events from being triggered.
If you experience performance issues in your application, it
may be because of the cost of event propagation if there is a
significantly large number of elements. In this case, setting
global
to false
may give you a performance
improvement.
The beforeSend
callback
is a local event that allows for modifying the XMLHttpRequest
object (which is passed in as
an argument) prior to the request being sent. In the following
example, we specify a custom HTTP header for the request. It is
possible to cancel the request by returning false
from the callback:
(function($) { $(document).ready(function() { // Start our ajax request when doAjaxButton is clicked $('#doAjaxButton').click(function() { $.ajax({ url: 'ajax-gateway.php', beforeSend: function(xmlHttpRequest) { xmlHttpRequest.setRequestHeader('X-SampleHeader', 'Hello world’); }, complete: function() { // Data processing code } }); }); }); })(jQuery);
Now taking all of the events if we revise our solution, we come up with the following:
(function($) { $(document).ready(function() { $('#ajaxError') .ajaxError(function(evt, xhr, ajaxOptions, error) { $(this) .html( 'Error: ' + ( xhr ? xhr.status : '' ) + ' ' + ( error ? error :'Unknown' ) ) .show(); }) .ajaxSuccess(function() { $(this).hide(); }); $('#ajaxStatus') .ajaxStart(function() { $(this).show(); }) .ajaxSend(function() { $(this).html('Sending request...'), }) .ajaxStop(function() { $(this).html('Request completed...'), var t = this; setTimeout(function() { $(t).hide(); }, 1500); }); // Start our ajax request when doAjaxButton is clicked $('#doAjaxButton').click(function() { $.ajax({ url: 'ajax-gateway.php', complete: function() { // Data processing code } }); }); }); })(jQuery);
You need to make a GET Ajax request to the server and
place the contents of the resulting HTML in a <div>
with an ID of contents
.
(function($) { $(document).ready(function() { $('#contents').load('hello-world.html'), }); })(jQuery);
This recipe differs slightly from others in that we’ll survey a variety of the functions and shortcuts provided by jQuery in an effort to clearly present their differences.
jQuery provides a number of shortcuts for making Ajax requests. Based upon the previous recipes covering Ajax, the
following shortcuts exist: .load(),
$.get(), $.getJSON(), $.getScript()
, and $.post()
. But first, let’s review our
solution:
$('#contents').load('hello-world.html'),
The .load(url)
method
is making a GET Ajax request to hello-world.html
and placing the contents of
that result into the element #contents
. Two optional parameters to the
.load()
method are data
and callback
. The data
parameter can be either a map (or
JavaScript object) or as of jQuery 1.3 a string. The following example
is passing in the variable hello
with the value world
. (This is the
same as the following URL: hello-world.html?hello=world
.)
$('#contents').load('hello-world.html', { hello: 'world' });
The third optional parameter is a callback function that is
called when the request completes (either on success
or error
). In the following example, an alert
message is being triggered upon the completion of the request:
$('#contents').load('hello-world.html', { hello: 'world' }, function() { alert('Request completed!'), });
The next two methods we will look at are $.get()
and $.post()
. Both methods accept the same arguments, with the
$.get()
method sending a GET HTTP
request and the $.post()
method
sending a POST HTTP request. We’ll look at a sample using the $.get()
request. The $.get()
method accepts url
, data
, callback
, and type
parameters. The first three parameters
function the same as with the previous load()
method, so we’ll only cover the final
type
parameter:
$.get( 'hello-world.html', { hello: 'world' }, function(data) { alert('Request completed!'), }, 'html' );
The type
parameter can accept one of the following: xml
, html
, script
, json
, jsonp
, or text
. These type
values determine how the response text
from the Ajax request is processed prior to being passed to the
callback function. In the previous example, since we specified a
type
of html
, the data argument of our callback will
be in DOM object form. Specifying xml
as the type
will result in an xml
DOM object being passed in. If you
specify script
as the type
, the resulting data returned by the
server will be executed prior to the callback
method being triggered. Both
json
and jsonp
formats result in a JavaScript object
being passed to your callback
method, with the difference of jsonp
being that jQuery will pass in a
method name in the request and map that callback method to the
anonymous function defined with the request. This allows for
cross-domain requests. Finally, the text
format is just as the name suggests:
plain text that is passed in as a string to your callback
method.
We’ll now look at the final two shortcuts: $.getJSON()
and $.getScript()
. The $.getJSON()
method accepts url
, data
, and callback
as arguments. $.getJSON()
is essentially a combination of
the $.get()
method with appropriate
parameters being set for JSON
or
JSONP
. The following example would
make a JSONP
request to Flickr and
request photos from the public timeline:
$.getJSON( 'http://www.flickr.com/services/feeds/photos_public.gne? format=json&jsoncallback=?’, function(json) { } );
Since this request is cross-domain, jQuery will automatically
treat the request as a JSONP
and
fill in the appropriate callback
function name. This also means that jQuery will initiate the request
by inserting a <script>
tag
into the document instead of using the XMLHttpRequest
object. Flickr’s API allows
for the callback
function name to
be specified by setting the jsoncallback
get variable. You’ll notice the
jsoncallback=?
portion of the URL.
jQuery will intelligently replace the ?
with the appropriate function name
automatically. By default jQuery will append a callback=
variable but allows for easily
modifying this, as demonstrated. The callback
replacement works on both GET and
POST request URLs but will not work with parameters passed in the data
object. See Recipes 16.7 and 16.8 for working with JSON and Recipe 16.9 for a full JSONP implementation of
our Flickr example.
$.getScript()
executes a
request either via an Ajax request or via dynamically inserting a
<script>
tag for cross-domain
support and then evaluating the returned data and finally triggering
the callback provided. In the following example, we’re adding a script
to the document and then calling one of the functions it provides in
the callback:
// hello-world.js function helloWorld(msg) { alert('Hello world! I have a message for you: ' + msg); } // hello-world.html (function($) { $(function() { $.getScript('hello-world.js', function() { helloWorld('It is a beautiful day!'), }); }); })(jQuery);
You want to take a string of HTML and convert it into a series of DOM nodes and then insert it into the document.
(function($) { $(document).ready(function() { $('<div>Hello World</div>') .append('<a href="http://jquery.com">A Link</a>') .appendTo('body'), }); })(jQuery);
Manipulating strings of HTML is one of the more common tasks when using jQuery. At the heart of jQuery is an elegant interface for translating a string of markup into its DOM representation. Instead of passing in a selector, we can simply pass in a string of HTML. (The following does not work for XML; see Recipe 16.6 for converting a string of XML to a DOM.)
$('<div>Hello World</div>'),
At this point our HTML has been converted into a DOM representation and is ready for manipulation with jQuery. We can operate on this fragment using any of the jQuery methods:
$('<div>Hello World</div>') .append('<a href="http://jquery.com">A Link</a>') .appendTo('body'),
One caveat worth noting is that prior to the HTML fragment
being appended to the document, some visual attributes such as
width
and height
may not be available. So in the
following example, calling .width()
will return a value of 0
.
$('<div>Hello World</div>').width(); // Returns '0'
<h1 id="title"></h1> (function($) { $(document).ready(function() { var xml = '<myxml><title>Hello world!</title></myxml>'; var title = $.xmlDOM( xml ).find('myxml > title').text(); $('#title').html( title ); }); })(jQuery);
A frequent question appearing on the jQuery mailing list is how
to convert a string of XML to its DOM representation that jQuery is
able to operate on. When making an Ajax request with a response type
of xml
, the browser will
automatically parse the returned XML text into a DOM object.
So, what would you do if you had a string of XML that you needed
to process with jQuery? The xmlDOM
plugin provides native cross-browser parsing of a string of XML and
returns a jQuery
-wrapped DOM
object. This allows you to convert and access the XML in one
step:
(function($) { $(document).ready(function() { var xml = '<myxml><title>Hello world!</title></myxml>'; var title = $.xmlDOM( xml ).find('myxml > title').text(); $('#title').html( title ); }); })(jQuery);
Another common practice is passing in the DOM object as the second argument to jQuery (the context) as follows:
(function($) { $(document).ready(function() { var $xml = $.xmlDOM( '<myxml><title>Hello world!</title></myxml>' ); var title = $('myxml > title', $xml).text(); $('#title').html( title ); }); })(jQuery);
This allows you to run your jQuery selection against the context object passed in; otherwise, jQuery runs the query against the document object.
The xmlDOM plugin by the author may be downloaded from http://jquery-cookbook.com/go/plugin-xmldom.
You have a JavaScript object that contains data that needs to be serialized for easier storage and retrieval.
(function($) { $(document).ready(function() { var messageObject = { title: 'Hello World!', body: 'It's great to be alive!' }; var serializedJSON = JSON.stringify( messageObject ); }); })(jQuery);
JavaScript Object Notation (JSON) is a common data format used to exchange data between the browser and the server. It is lightweight in nature and is easy to use and parse in JavaScript. Let’s first look at a simple object:
var messageObject = { title: 'Hello World!', body: 'It's great to be alive!' };
In this example, we have a simple object with two attributes,
title
and body
. Being able to store a serialized
version of the object is quite simple. The serialized version is as
follows:
var serializedJSON = '{"title":"Hello World!","body":"It's great to be alive!"}';
The two common tasks when working with JSON are serialization
(encoding an object into a string representation) and
deserialization (decoding a string representation into an object
literal). Currently, only a handful of browsers have native built-in
JSON handling (such as Firefox 3.1+ and Internet Explorer 8). Other
browsers have plans to add support because JSON is now part of the
ECMA 3.1 specification. In the meantime, there are two main approaches
to take when working with JSON data. Douglas Crockford has written a JavaScript
implementation for encoding and decoding JSON, which you can get from
http://jquery-cookbook.com/go/json. Let’s
serialize the previous object utilizing the JSON
library:
var serializedJSON = JSON.stringify( messageObject );
We now have a string representation that we can send to our server such as in an Ajax request or submit in a form.
(function($) { $(document).ready(function() { var serializedJSON = '{"title":"Hello World!","body":"It's great to be alive!"}'; var message = JSON.parse( serializedJSON ); }); })(jQuery);
As discussed in the previous recipe, we’ll now look at parsing or decoding a JSON string.
It is important to note that some of the approaches outlined here are unsafe and may cause potential security issues. Make sure that you trust the source of your data.
The easiest approach in consuming JSON data is to eval()
the message. There are some inherent security issues
though with this approach because eval()
encompasses the entire JavaScript
specification instead of simply the JSON subset. What this means is
that a malicious person could execute code embedded in the JSON
string. So, we don’t recommend this approach. Instead, let’s use
Douglas Crockford’s JSON
library
mentioned in the previous recipe. (Note that his library does utilize
eval()
except that it pre-processes
the data to make sure the data is safe.)
var serializedJSON = '{"title":"Hello World!","body":"It's great to be alive!"}'; var message = JSON.parse( serializedJSON );
So, now we can work with our message object as we would any other JavaScript object:
alert( "New Message! Title: " + message.title + " Body: " + message.body);
The JSON
library as well as
additional JSON resources may be downloaded from http://jquery-cookbook.com/go/json.
You want to consume a list of photos from Flickr’s public photo stream and display the first three images.
(function($) { $(document).ready(function() { var url = 'http://www.flickr.com/services/feeds/photos_public.gne? jsoncallback=?'; var params = { format: 'json' }; $.getJSON(url, params, function(json) { if ( json.items ) { $.each( json.items, function(i, n) { var item = json.items[i]; $('<a href="' + item.link + '"></a>') .append('<img src="' + item.media.m + '" />') .appendTo('#photos'), // Display the first 3 photos (returning false will // Exit the loop return i < 2; }); } }); }); })(jQuery);
Security is a critical issue when building a website or
application and especially so with the advent of Ajax. Web browsers
have enforced a same origin policy on requests, which means that
you’re restricted to making requests to the same domain as the page’s
URL or a subdomain of the current domain. So, for example, a page
served from http://www.example.com is allowed to
make Ajax requests to http://www.example.com and
http://x.www.example.com but not
http://example.com or
http://y.example.com. As the semantic Web emerged
and websites such as Flickr started exposing an API for other users
and services to consume, the security policy enforced by web browsers
became a hindrance. One area that has never had same origin policies
was the use of the script element with a src
attribute. It’s possible for
http://www.example.com to include a script from
http://static.example2.com, but the issue of
dynamically including that script and managing program flow became an
issue. Thus, JSONP emerged as a standard to overcome the same origin
limitation.
It is important to note that some of the approaches outlined here are unsafe and may cause potential security issues. Make sure that you trust the source of your data. Also, when including a script element in a page, that entire script will have access to the entire HTML DOM and any private or sensitive data that it may contain. It could be possible for a malicious script to send this data to an untrusted party. Take extra precaution such as placing the script in a sandbox. Extended security is outside the scope of this recipe, but we wanted to make sure you were aware of it.
JSONP makes use of requesting data through a <script>
tag with a src
attribute as well as manages the program
flow for the developer by wrapping the data in a callback function
that the developer can implement. Let’s first look at a sample JSON
message.
{"title":"Hello World!","body":"It's great to be alive!"}
Here is the same message wrapped in a callback:
myCallback({"title":"Hello World!","body":"It's great to be alive!"})
What this allows for is when the resource is requested upon
being loaded in the browser the myCallback
function will be called and have
the JSON object passed in as the first argument. The developer can
then implement the myCallback
function as follows to process the data:
function myCallback(json) { alert( json.title ); }
Now let’s review our Flickr solution. First we define the URL of
the Flickr web service followed by declaring a params
object for the get variables. The
jsoncallback
param is a special
param defined by the Flickr service that allows for us to pass in a
function name. Since we’ve set this parameter to a ?
, jQuery will automatically generate a
function name and bind it with our callback method.
jQuery detects that this is a JSONP (cross-domain request) by
the =?
in the URL. It is not
possible to pass this in the params
array.
var url = 'http://www.flickr.com/services/feeds/photos_public.gne? jsoncallback=?'; var params = { format: 'json' };
Next, we call jQuery’s $.getJSON()
method, passing in our url
,
params
, and our callback function,
which will accept a JSON object. In our callback method, we check and
make sure that an item’s array exists and then use jQuery’s $.each()
to iterate over the first three
items, create a link, append an image to it, and then append the link
to an element with an ID of photos
.
Finally, our callback function will return false
on the third iteration (when i = 2
), breaking the loop.
$.getJSON(url, params, function(json) { if ( json.items ) { $.each( json.items, function(i, n) { var item = json.items[i]; $('<a href="' + item.link + '"></a>') .append('<img src="' + item.media.m + '" />') .appendTo('#photos'), return i < 2; }); } });
With the combination of the JSON data format and the cross-domain capabilities of JSONP, web developers are able to create new applications aggregating and transforming data in new and innovative ways and growing the semantic Web.
3.149.236.27