Applying Ajax with jQuery

1. REVISITING AJAX BASICS

The definition of Ajax has evolved from its original acronym of Asynchronous JavaScript and XML, a method for sending and retrieving data (using XML) to a server asynchronously without the need to completely reload a web document. It now refers to methods for creating interactive web and dynamic applications with JavaScript, as you’ve seen in previous chapters, as well as asynchronous communication between the client and server. These days it’s not always XML that’s used to transport information between the server and client, but often other formats such as JSON.

At the heart of Ajax is the XmlHttpRequest object, which dates back to 2000 when Microsoft introduced an ActiveX object for asynchronous communication between server and client. In 2005, the term “Ajax” was coined by Jesse James Garrett to describe this technique.

The same disparity between the way browsers manage events is also observed with Ajax, albeit not as complex. For example, to create a new request object in Internet Explorer, you would do the following:

var xhr = new ActiveXObject(“Microsoft.XMLHTTP”);

or in even older versions of IE:

var xhr = new ActiveXObject(“MSXML2.XMLHTTP”);

In IE7 or above and any other browser that supports XMLHttpRequest, you use the native JavaScript XMLHttpRequest object:

var xhr = new XMLHttpRequest();

Luckily, the methods used between the standard XMLHttpRequest object and the Microsoft ActiveX object are remarkably similar, hence simplifying matters. Typically, if you were to handle the details yourself you might use bit of code like this to instantiate a request object:

/*

Support for the original ActiveX object in older versions of Internet Explorer

This works all the way back to IE5.

*/

if ( typeof XMLHttpRequest == “undefined” )       {

XMLHttpRequest = function ()     {

try {

return new ActiveXObject( “Msxml2.XMLHTTP.6.0” );

}

catch (e)  {}

try {

return new ActiveXObject( “Msxml2.XMLHTTP.3.0” );

}

catch (e)   {}

try {

return new ActiveXObject( “Msxml2.XMLHTTP” );

}

catch (e)   {}

throw new Error( “No XMLHttpRequest.” );

};

}

var xhr = new XMLHttpRequest();

Once the details of creating the request object have been taken care of, a connection must be established with the server, using the xhr.open() method, which follows the form:

xhr.open(method, url, isAsynchronous);

where method refers to the HTTP method, the action to be performed on the specified resource. While the full list of HTTP methods (or “verbs”) is much longer, Ajax requests will typically be a POST or a GET. The second parameter is the url used to exchange data, and isAsynchronous is a Boolean flag indicating whether or not the call is asynchronous. This flag indicates whether or not the request should block the execution of the rest of the script. As the name of the technique implies, this will normally be set to true. So, you can use the request object to make synchronous posts to the server, but that would be defeating the whole purpose of Ajax. After opening the connection, use the xhr.send() method to initiate the request to the server. It accepts a data argument:

xhr.open(“POST”, “/request/url/”, true);

xhr.send(data);

Because the communication is asynchronous you can’t count on sequential execution of methods. For example:

xhr.send(data);

doSomething();

The send method is executed and returns immediately, whereas the request and response each operate on separate threads. You won’t have the response from the server when doSomething(); gets executed. The solution is to use the callback handler onreadystatechange, which is invoked when there is a request state change. So, the previous snippet can be rewritten as:

xhr.onreadystatechange = function(){

if( this.readyState === 4 ){

if( this.status >== 200 && this.status < 300 ){

doSomething();

} else {

// problem with communication

}

}

}

xhr.send( data );

Now, when the request is completed and successful, doSomthing() is executed. Table 6-2 shows the different ready states.

After the request is completed and successful, you can get the response using the xhr object’s responseText method. What the responseText returns is dependent on the server-side code. It can be text of any format, not just XML. Now given that, you can expect any data format to come from the server — HTML, XML, JSON, plain text, and so on, each with its own pros and cons for parsing data returned.

The major trend these days is toward using JSON at the data interchange format of choice. Since it’s a native JavaScript object, people find it much easier to deal with programmatically. Instead of firing up DOM traversal methods to walk through the XML DOM returned by the server, you can simply use dot notation to access properties of the JSON response. Also, JSON is generally less verbose than XML so it lowers the cost of each request over the wire.

You’ll still see XML, of course, and there are uses for plain text and even full blocks of HTML, but it’s much more common to see JSON these days.

As you’ve no doubt expected, jQuery offers alternative ways of handling Ajax request and response in a more simplified manner.

2. APPLYING AJAX WITH JQUERY

Just as you saw in the previous chapter with event handling, jQuery abstracts away many of the details of Ajax communication. For example, you don’t have to worry about how the xhr object is instantiated. The general jQuery method used for sending Ajax calls is (drum roll…) the $.ajax() method, which takes the form:

$.ajax(url, [settings]);

Or a single settings object:

$.ajax([settings]);

Many of the following examples require a server to respond to the Ajax calls. The samples include both Python and PHP backends to support the frontend; both are freely available. Unfortunately, explanation of either of these technologies is beyond the scope of this book.

Table 6-3 lists some of the most important available settings. For a full list check out the online documentation at http://api.jquery.eom/jQuery.ajax/#jQuery-ajax-settings.

The following example sends a post to the URL /post/2/here.html, which receives some plain text, and logs the result on success, or displays another log on error:

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”></script>

<script type=”text/javascript”>

$.ajax({

url : “/post/2/here.html”, dataType: “html”, success: function(r){

console.log( “made it” );

},

error : function(r){

console.log( “Something didn’t work” );

}

})

</script>

</head>

<body>

</body>

</html>

Valid types of data that can be returned are XML, HTML, JSON, JSONP, text and script.

As you’ve seen in previous chapters, jQuery is particularly adept at traversing DOM trees. In a situation where the server response is XML, jQuery has the $.find() method for getting data from an XML document. Say you have an Ajax call that returns an XML document that describes a book, and you’d like to retrieve the chapter titles. The following code illustrates a use of $.find() to parse some XML data and append it into the DOM.

<!DOCTYPE html>

<html>

<head>

<scriptsrc=”http://code.jquery.com/jquery-1.7.1.js”></script>

<script type=”text/javascript”>

$.ajax({

url : “/book.html”,

dataType: “xml”,

success: function( data ){

$( data ).find( “chapter” ).each(function() {

$( “document” ).append($( this ).find( “title” ).text()); });

},

error : function(data){

console.log( “unable to process request” );

}

});

</script>

</head>

<body>

</body>

</html>

Code snippet is from ajax-find.txt

This example appends the titles found to the document body. The .find() method searches an XML document for the element name specified. Just like an HTML document, if you want to access an attribute use the .attr() method.

These days it’s fairly common to request data in JSON format, which is completely logical considering that JSON objects are JavaScript objects. They also have the advantage of being lighter-weight than XML data. You could use the $.ajax() method to obtain a JSON object from the server like this:

$.ajax({

url: “/jsonBook.html”,

dataType:  ‘json’,

data: data,

success: function( data ){

// process json data

}

});

But, jQuery provides a shortcut method for obtaining data from the server asynchronously in JSON format — it’s called $.getJSON():

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”></script>

<script type=”text/javascript”>

$.getJSON(“/url/2/get/json.php”, function(data, text, jqXHR){

// handle callback here });

</script>

</head>

<body>

</body>

</html>

Unfortunately, there is no equivalent $.postJSON() method.

By now, you’ll notice that many of your applications will use a common set of options. jQuery provides a method that globally sets the Ajax options called $.ajaxSetup(). These values will be used in any call to $.ajax() unless overridden. $.ajaxSetup() takes the form:

$.ajaxSetup(options);

where options is an object instance whose properties are used to set the global Ajax properties. For example, to set the default type, dataType, and error callback:

$.ajaxSetup({

type : “GET”,

dataType : “json”,

error : universalGoofupFunction

});

It should be noted that $.ajaxSetup() overrides the settings for all Ajax requests on the page so it should be used sparingly unless you’re in complete control of all the JavaScript code running on the page.

But, before continuing this is a good time to review a feature introduced in jQuery 1.5, which is deferred objects. These objects are an example of the promise pattern. A promise is an object that “promises” to give a value at a convenient future time; this is particularly useful for asynchronous computing. jQuery deferred objects, also based on the promise pattern, allow chainable callback functions.

Deferred objects begin in the unresolved state, and can change from unresolved to either resolved or rejected. Deferred objects are covered in depth in Chapter 13.

The $.ajax() method, indeed all jQuery Ajax methods, returns a promise object called a jqXHR object, which is a superset of the XmlHTTPRequest object. This allows Ajax calls to be rewritten, now, as:

$.ajax({url : “/go/here”})

.success(function(){ /* handle success */ })

.complete(function(){ /* handle complete method */ });

There are some shortcut utility methods as well, $.get() and $.post(), which send get and post requests, respectively. In the case of $.get(), instead of having to write:

$.ajax({

type: “get”,

url: “/go/here.php”,

data: data,

success: callBackFunction,

dataType: “json”

});

you can instead type:

var jqxhr = $.get( “/go/here.php”, function(data){} );

The general format is:

$.get( url, parameters, callback, type );

Just like $.ajax(), the $.get() method returns a jqXHR object. The corresponding shortcut for posts, $.post(), is almost identical, except for the type, of course:

$.post( url, parameters, callback, type );

jQuery also has an incredibly useful method, $.load(), for making Ajax requests and loading HTML fragments into a matched element, all in the same call. It takes the following form:

$( selector ).load( url, [data], [success(response, status, XHR)] )

It accepts a target url, an optional data object, and an optional success object, which itself will receive a response text, a status string, and an XMLHttpRequest object. For example:

$( “#latestNews” ).load( “/getLatest.php”, dateObj, function(resp, status, XHR){

if(status === “success”){

// handle here

}

});

Assuming all went well on the server, the latestNews element will be loaded with the text obtained from the server.

The last example of this chapter is a pizza order form, and uses the new HTML5 controls and jQuery’s $.load() Ajax method. To accommodate customers using older browsers, you’ll employ jQuery to fill in the functionality, but also to validate input and post the order.

As a note, this sample doesn’t handle the case of users without JavaScript. While that’s an important factor to keep in mind when designing web forms, the majority of the hard work to do that properly (setting up a fallback validation service on the server) is outside the scope of a book like this.

The customer is not required to register with the pizza shop in order to get a pizza, but must fill out several fields. The order form will have the following fields:

  • first name
  • last name
  • address
  • state
  • zip
  • phone
  • e-mail
  • time of delivery
  • cc type
  • cc number
  • pizza size
  • toppings

First design the form, and instead of using labels to indicate each field, use placeholder text. All fields are required except for pizza toppings. The state field will use an Ajax autocomplete feature. In order to sniff out the browser features, you’ll employ Modernizr again. To bring things around full circle, add the Data Link plugin to shorten the process of getting data from the form fields to the server. After posting to the server, if no exception is thrown, the customer is shown a receipt of the order.

The following code listing shows the markup. Remember that the full source code for this example is available at the book’s website; no need to type all of this!

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”></script>

<script src=”http://ajax.cdnjs.com/ajax/libs/modernizr/1.7/modernizr-1.7.min.js”> </script>

<script src=”jquery.datalink.js”></script>

<script type=”text/javascript”>

//Application code will go here.

</script>

</head>

<body>

<h1>Enter Order Information</h1>

<form id=”pizzaOrderForm”>

<input type=”text” name=”firstName” placeholder=”First Name” required >

<input type=”text” name=”lastName” placeholder=”Last Name” required >

<input type=”text” name=”address” placeholder=”Address” required >

<input type=”text” name=”state” placeholder=”State” required >

<input type=”text” name=”zip” placeholder=”Zip Code” required >

<input type=”tel” name=”phone”

pattern=”999-999-9999″ placeholder=”Phone” required >

<input type=”email” name=”email” placeholder=”Email” required >

<input type=”text” name=”timeOfDeliver” placeholder=”Time to Deliver” required > <select name=”ccType”>

<option value=”visa”>Visa <option value=”amex”>AmEx <option value=”discover”>Discover </select>

<input type=”text” name=”ccNumber” placeholder=”CC Number” required >

<input type=”text” name=”pizzaSize” placeholder=”” required >

<select name=”pizzaSize”>

<option value=”0″>Pick a size

<option value=”1″>personal

<option value=”2″>small

<option value=”3″>medium

<option value=”4″>large

<option value=”5″>sportsman

</select>

<label> Number of Pizzas

<input type=”number” name=”numOfPizzas” min=”0″ max=”99″

value=”0″ placeholder=”Zip Code” required ></label>

<label>Pepperoni<input type=”checkbox” name=”toppings” value=”pepperoni”></label>

<label>Garlic<input type=”checkbox” name=”toppings” value=”garlic”></label>

<label>Mushroom<input type=”checkbox” name=”toppings” value=”mushrooms”></label>

<label>Sausage<input type=”checkbox” name=”toppings” value=”sausage”></label>

<input type=”button” value=”Order!” >

</form>

<div id=”price”></div>

</body>

</html>

Building on previous examples from this chapter, this example uses the Modernizr library to determine if the email input, required, and placeholder features are present. As you’ll see in the JavaScript that follows, when any of the features aren’t present, jQuery is employed to fill in the missing functionality.

In order to auto-fill in the order object, the Data Link plugin is used, thereby removing some intermediate steps. This is as simple as creating an empty object that will serve as the container for the field data, selecting the form, and calling the inherited link method on the wrapper object.

var order = {};

$(“#pizzaOrderForm”).link(order);

We use the traditional submit method of the form and add a simple test to see if the required fields have been filled in browsers that don’t support the required attribute. The $.load() method is used to make an order to the server. The response text returned is an HTML string, which is inserted inside of the #price div, avoiding the need to append text. The following code block demonstrates the complete JavaScript example.

$( “form” ).bind( “submit”, function( e ){

var valid = true;

if(!Modernizr.input.required){

$( “input[required]” ).each(function(){

if ( $( this ).val() === “” ) {

$( “#reqMessage” ).show();

$( this ).css( “border” , “1px solid red” );

valid = false;

}

});

}

e.preventDefault();

if (valid) {

$( “#price” ).load( “/process”, data, function(responseTxt, status, XHR ){

if(status === “success”){

$( “[type=button]” ).attr( “disabled” );

} else if(status === “error”){

$( “#price” ).append( “unable to process request, game over man” );

}

});

}

});

<script type=”text/javascript”>

$(function(){

// for browsers that don’t yet process HTML5 //

// Placeholder implementation

if(!Modernizr.input.placeholder){

$(‘input[type=text]’).each(function(){

$( this ).val($( this ).attr(‘placeholder’));

}); $(‘input[type=text]’).focus(function(){

if($( this ).val() === $( this ).attr(‘placeholder’)){

$( this ).val(”);

}

});

$(‘input[type=text]’).blur(function(){

if($( this ).val() === ”){

$( this ).val($( this ).attr(‘placeholder’));

}

});

}

// required implementation

if(!Modernizr.input.required){

var $msg = $( “<div id=’reqMessage’>Required Fields Missing</div>” );

$msg.css( “background-color”, “yellow” )

.hide();

$( “body” ).append( $msg );

}

// email input implementation

var emailRegEx = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;

if( !Modernizr.inputtypes.email ){

$(‘input[type=url]’).blur( function(){

var emailValue = $( this ).val();

if( emailValue !== ”){

var passes = emailRegEx.test(emailValue);

if( !passes ){

// display validation error message $( “#errorDiv” ).show();

// disable submit button

$( “input[type=’submit’]” ).attr( “disabled” );

} else {

$( “#errorDiv” ).hide();

$( “input[type=’submit’]” ).attr( “disabled”,”” );

}

}

});

}

// ordering stuff

var order = {};

$( “#pizzaOrderForm” ).link( order );

$( “form” ).bind( “submit”, function( e ){

var valid = true;

if(!Modernizr.input.required){

$( “input[required]” ).each(function(){

if ( $( this ).val() === “” ) {

$( “#reqMessage” ).show();

$( this ).css( “border” , “1px solid red” ); valid = false;

}

});

}

e.preventDefault(); if (valid) {

$( “#price” ).load( “/process”, data, function(responseTxt, status, XHR ){

if(status === “success”){

$( “[type=button]” ).attr( “disabled” );

} else if(status === “error”){

$( “#price” ).append( “unable to process request, game over man” );

}

});

}

});

});

</script>

</head>

Code snippet is from pizza-form.txt

Figure 6-3 shows a side-by-side comparison of the same form with Opera 11, which supports HTML5, and the older Firefox 2, which does not.

Source: Otero Cesar, Rob Larsen (2012), Professional jQuery, John Wiley & Sons, Inc

Leave a Reply

Your email address will not be published. Required fields are marked *