CODEDIGEST
Home » Articles
Search
 

Technologies
 

Sponsored links
 

CodeDigest Navigation
 

Technology News
No News Feeds available at this time.
 

Community News
No News Feeds available at this time.
 
How to Bridge the Client-Server Gap using AJAX and jQuery-Part 1

By Jonathan Chaffer/Karl Swedberg
Posted On Mar 20,2009
Article Rating:
Be first to rate
this article.
No of Comments: 0
Category: jQuery
Print this article.

How to Bridge the Client-Server Gap using AJAX and jQuery-Part 1

 

In recent years, it has become common to judge sites based on their use of specific technologies. One of the most prominent buzzwords used to describe new web applications is AJAX-powered. This label has been used to mean many different things, as the term encompasses a group of related capabilities and techniques. This is covered in a two part series. 

 

In this article by Jonathan Chaffer and Karl Swedberg, we will learn :

 

Ø       The technologies involved

Ø       Loading data on demand

Ø       Choosing a data format

Ø       Passing data to the server

Ø       Keeping an eye on the request

Technically, AJAX is an acronym standing for Asynchronous JavaScript and XML. The technologies involved in an AJAX solution include:

Ø       JavaScript, to capture interactions with the user or other browser-related events

Ø       The XMLHttpRequest object, which allows requests to be made to the server without interrupting other browser tasks

Ø       XML files on the server, or often other similar data formats such as HTML or JSON

Ø       More JavaScript, to interpret the data from the server and present it on the page

Many frameworks have sprung up to assist developers in taming it, because of the inconsistencies in the browsers' implementations of the XMLHttpRequest object; jQuery is no exception.

Loading data on demand

Underneath all the hype and trappings, AJAX is just a means of loading data from the server to the web browser, or client, without a visible page refresh. This data can take many forms, and we have many options for what to do with it when it arrives. We'll see this by performing the same basic task in many ways.

We are going to build a page that displays entries from a dictionary, grouped by the starting letter of the dictionary entry. The HTML defining the content area of the page will look like this:

<div id="dictionary">
</div>

Yes, really! Our page will have no content to begin with. We are going to use jQuery's various AJAX methods to populate this <div> with dictionary entries.

<div class="letters">
  <div class="letter" id="letter-a">
    <h3><a href="#">A</a></h3>
  </div>
  <div class="letter" id="letter-b">
    <h3><a href="#">B</a></h3>
  </div>
  <div class="letter" id="letter-c">
    <h3><a href="#">C</a></h3>
  </div>
  <div class="letter" id="letter-d">
    <h3><a href="#">D</a></h3>
  </div>
</div>

As always, a real-world implementation should use progressive enhancement to make the page function without requiring JavaScript. Here, to simplify our example, the links do nothing until we add behaviors to them with jQuery.

As always, a real-world implementation should use progressive enhancement to make the page function without requiring JavaScript. Here, to simplify our example, the links do nothing until we add behaviors to them with jQuery.

Adding a few CSS rules, we get a page that looks like this:

jquery_article2_image01.png

Now we can focus on getting content onto the page.

Appending HTML

AJAX applications are often no more than a request for a chunk of HTML. This technique, sometimes referred to as AHAH (Asynchronous HTTP and HTML), is almost trivial to implement with jQuery. First we need some HTML to insert, which we'll place in a file called a.html alongside our main document. This secondary HTML file begins:

<div class="entry">
  <h3 class="term">ABDICATION</h3>
  <div class="part">n.</div>
  <div class="definition">
    An act whereby a sovereign attests his sense of the high
       temperature of the throne.
    <div class="quote">
      <div class="quote-line">Poor Isabella's Dead, whose
              abdication</div>
      <div class="quote-line">Set all tongues wagging in the
              Spanish nation.</div>
      <div class="quote-line">For that performance 'twere
              unfair to scold her:</div>
      <div class="quote-line">She wisely left a throne too
              hot to hold her.</div>
      <div class="quote-line">To History she'll be no royal
              riddle &mdash;</div>
      <div class="quote-line">Merely a plain parched pea that
              jumped the griddle.</div>
      <div class="quote-author">G.J.</div>
    </div>
  </div>
</div>

<div class="entry">
  <h3 class="term">ABSOLUTE</h3>
  <div class="part">adj.</div>
  <div class="definition">
    Independent, irresponsible.  An absolute monarchy is one
       in which the sovereign does as he pleases so long as he
       pleases the assassins.  Not many absolute monarchies are
       left, most of them having been replaced by limited
       monarchies, where the sovereign's power for evil (and for
       good) is greatly curtailed, and by republics, which are
       governed by chance.
  </div>
</div>

The page continues with more entries in this HTML structure. Rendered on its own, this page is quite plain:

jquery_article2_image02.png

Note that a.html is not a true HTML document; it contains no <html>, <head>, or <body>, all of which are normally required. We usually call such a file a snippet or fragment; its only purpose is to be inserted into another HTML document, which we'll accomplish now:

$(document).ready(function() {
  $('#letter-a a').click(function() {
    $('#dictionary').load('a.html');
    return false;
  });
});

The .load() method does all our heavy lifting for us! We specify the target location for the HTML snippet by using a normal jQuery selector, and then pass the URL of the file to be loaded as a parameter to the method. Now, when the first link is clicked, the file is loaded and placed inside <div id="dictionary">. The browser will render the new HTML as soon as it is inserted:

jquery_article2_image03.png

Note that the HTML is now styled, whereas before it was plain. This is due to the CSS rules in the main document; as soon as the new HTML snippet is inserted, the rules apply to its tags as well.

When testing this example, the dictionary definitions will probably appear instantaneously when the button is clicked. This is a hazard of working on our applications locally; it is hard to account for delays in transferring documents across the network. Suppose we added an alert box to display after the definitions are loaded:

$(document).ready(function() {
  $('#letter-a a').click(function() {
    $('#dictionary').load('a.html');
    alert('Loaded!');
    return false;
  });
});

However, when this particular code is tested on a production web server, the alert will quite possibly have come and gone before the load has completed, due to network lag. This happens because all AJAX calls are by default asynchronous. Otherwise, we'd have to call it SJAX, which hardly has the same ring to it! Asynchronous loading means that once the HTTP request to retrieve the HTML snippet is issued, script execution immediately resumes without waiting. Sometime later, the browser receives the response from the server and handles it. This is generally desired behavior; it is unfriendly to lock up the whole web browser while waiting for data to be retrieved.

If actions must be delayed until the load has been completed, jQuery provides a callback for this. An example will be provided below.

Working with JavaScript objects

Pulling in fully-formed HTML on demand is very convenient, but there are times when we want our script to be able to do some processing of the data before it is displayed. In this case, we need to retrieve the data in a structure that we can traverse with JavaScript.

With jQuery's selectors, we could traverse the HTML we get back and manipulate it, but it must first be inserted into the document. A more native JavaScript data format can mean even less code.

Retrieving a JavaScript object

As we have often seen, JavaScript objects are just sets of key-value pairs, and can be defined succinctly using curly braces ({}). JavaScript arrays, on the other hand, are defined on the fly with square brackets ([]). Combining these two concepts, we can easily express some very complex and rich data structures.

The term JavaScript Object Notation (JSON) was coined by Douglas Crockford to capitalize on this simple syntax. This notation can offer a concise alternative to the sometimes-bulky XML format:

{
  "key": "value",
  "key 2": [
    "array",
    "of",
    "items"
  ]
}

For information on some of the potential advantages of JSON, as well as implementations in many programming languages, visit http://json.org/ .

We can encode our data using this format in many ways. We'll place some dictionary entries in a JSON file we'll call b.json, which begins as follows:

[
  {
    "term": "BACCHUS",
    "part": "n.",
    "definition": "A convenient deity invented by the...",
    "quote": [
      "Is public worship, then, a sin,",
      "That for devotions paid to Bacchus",
      "The lictors dare to run us in,",
      "And resolutely thump and whack us?"
    ],
    "author": "Jorace"
  },
  {
    "term": "BACKBITE",
    "part": "v.t.",
    "definition": "To speak of a man as you find him when..."
  },
  {
    "term": "BEARD",
    "part": "n.",
    "definition": "The hair that is commonly cut off by..."
  },

To retrieve this data, we'll use the $.getJSON() method, which fetches the file and processes it, providing the calling code with the resulting JavaScript object.

Global jQuery functions

To this point, all jQuery methods that we've used have been attached to a jQuery object that we've built with the $() factory function. The selectors have allowed us to specify a set of DOM nodes to work with, and the methods have operated on them in some way. This $.getJSON() function, however, is different. There is no logical DOM element to which it could apply; the resulting object has to be provided to the script, not injected into the page. For this reason, getJSON() is defined as a method of the global jQuery object (a single object called jQuery or $ defined once by the jQuery library), rather than of an individual jQuery object instance (the objects we create with the $() function).

If JavaScript had classes like other object-oriented languages, we'd call $.getJSON() a class method. For our purposes, we'll refer to this type of method as a global function; in effect, they are functions that use the jQuery namespace so as not to conflict with other function names.

To use this function, we pass it the file name as before:

$(document).ready(function() {
  $('#letter-b a').click(function() {
    $.getJSON('b.json');
    return false;
  });
});

This code has no apparent effect when we click the link. The function call loads the file, but we have not told JavaScript what to do with the resulting data. For this, we need to use a callback function.

The $.getJSON() function takes a second argument, which is a function to be called when the load is complete. As mentioned before, AJAX calls are asynchronous, and the callback provides a way to wait for the data to be transmitted rather than executing code right away. The callback function also takes an argument, which is filled with the resulting data. So, we can write:

$(document).ready(function() {
  $('#letter-b a').click(function() {
    $.getJSON('b.json', function(data) {
    });
    return false;
  });
});

Here we are using an anonymous function as our callback, as has been common in our jQuery code for brevity. A named function could equally be provided as the callback.

Inside this function, we can use the data variable to traverse the data structure as necessary. We'll need to iterate over the top-level array, building the HTML for each item. We could do this with a standard for loop, but instead we'll introduce another of jQuery's useful global functions, $.each(). Instead of operating on a jQuery object, this function takes an array or map as its first parameter and a callback function as its second. Each time through the loop, the current iteration index and the current item in the array or map are passed as two parameters to the callback function.

$(document).ready(function() {
  $('#letter-b a').click(function() {
    $.getJSON('b.json', function(data) {
      $('#dictionary').empty();
      $.each(data, function(entryIndex, entry) {
        var html = '<div class="entry">';
        html += '<h3 class="term">' + entry['term'] + '</h3>';
        html += '<div class="part">' + entry['part'] + '</div>';
        html += '<div class="definition">';
        html += entry['definition'];
        html += '</div>';
        html += '</div>';
        $('#dictionary').append(html);
      });
    });
    return false;
  });
});

Before the loop, we empty out <div id="dictionary"> so that we can fill it with our newly-constructed HTML. Then we use $.each() to examine each item in turn, building an HTML structure using the contents of the entry map. Finally, we turn this HTML into a DOM tree by appending it to the <div>.

This approach presumes that the data is safe for HTML consumption; it should not contain any stray < characters, for example.

All that's left is to handle the entries with quotations, which takes another $.each() loop:

$(document).ready(function() {
  $('#letter-b a').click(function() {
    $.getJSON('b.json', function(data) {
      $('#dictionary').empty();
      $.each(data, function(entryIndex, entry) {
        var html = '<div class="entry">';
        html += '<h3 class="term">' + entry['term'] + '</h3>';
        html += '<div class="part">' + entry['part'] + '</div>';
        html += '<div class="definition">';
        html += entry['definition'];
        if (entry['quote']) {
          html += '<div class="quote">';
          $.each(entry['quote'], function(lineIndex, line) {
            html += '<div class="quote-line">' + line + '</div>';
          });
          if (entry['author']) {
            html += '<div class="quote-author">' + entry['author'] +
 '</div>';
          }
          html += '</div>';
         }
         html += '</div>';
         html += '</div>';
         $('#dictionary').append(html);
      });
    });
    return false;
   });
});

With this code in place, we can click the B link and confirm our results:

jquery_article2_image04.png

The JSON format is concise, but not forgiving. Every bracket, brace, quote, and comma must be present and accounted for, or the file will not load. In most browsers, we won't even get an error message; the script will just silently fail.

Executing a script

Occasionally we don't want to retrieve all the JavaScript we will need when the page is first loaded. We might not know what scripts will be necessary until some user interaction occurs. We could introduce <script> tags on the fly when they are needed, but a more elegant way to inject additional code is to have jQuery load the .js file directly.

Pulling in a script is about as simple as loading an HTML fragment. In this case, we use the global function $.getScript(), which, like its siblings, accepts a URL locating the script file:

$(document).ready(function() {
  $('#letter-c a').click(function() {
    $.getScript('c.js');
    return false;
  });
});

Scripts fetched in this way are run in the global context of the current page. This means they have access to all globally-defined functions and variables, notably including jQuery itself. We can therefore mimic the JSON example to prepare and insert HTML on the page when the script is executed, and place this code in c.js:.

var entries = [
  {
    "term": "CALAMITY",
    "part": "n.",
    "definition": "A more than commonly plain and..."
  },
  {
    "term": "CANNIBAL",
    "part": "n.",
    "definition": "A gastronome of the old school who..."
  },
  {
    "term": "CHILDHOOD",
    "part": "n.",
    "definition": "The period of human life intermediate..."
  },
  {
    "term": "CLARIONET",
    "part": "n.",
    "definition": "An instrument of torture operated by..."
  },
  {
    "term": "COMFORT",
    "part": "n.",
    "definition": "A state of mind produced by..."
  },
  {
    "term": "CORSAIR",
    "part": "n.",
    "definition": "A politician of the seas."
  }
];

var html = '';

$.each(entries, function() {
  html += '<div class="entry">';
  html += '<h3 class="term">' + this['term'] + '</h3>';
  html += '<div class="part">' + this['part'] + '</div>';
  html += '<div class="definition">' + this['definition'] + '</div>';
  html += '</div>';
});

$('#dictionary').html(html);

Now clicking on the C link has the expected result:

jquery_article2_image05.png

Loading an XML document

XML is part of the acronym AJAX, but we haven't actually loaded any XML yet. Doing so is straightforward, and mirrors the JSON technique fairly closely. First we'll need an XML file d.xml containing some data we wish to display, excerpted here:

<?xml version="1.0" encoding="UTF-8"?>
<entries>
  <entry term="DEFAME" part="v.t.">
    <definition>
      To lie about another.  To tell the truth about another.
    </definition>
  </entry>
  <entry term="DEFENCELESS" part="adj.">
    <definition>
      Unable to attack.
    </definition>
  </entry>
    <entry term="DELUSION" part="n.">
    <definition>
      The father of a most respectable family, comprising
      Enthusiasm, Affection, Self-denial, Faith, Hope,
      Charity and many other goodly sons and daughters.
    </definition>
    <quote author="Mumfrey Mappel">
      <line>All hail, Delusion!  Were it not for thee</line>
      <line>The world turned topsy-turvy we should see;
        </line>
      <line>For Vice, respectable with cleanly fancies,
        </line>
      <line>Would fly abandoned Virtue's gross advances.
        </line>
    </quote>
  </entry>
  <entry term="DIE" part="n.">
    <definition>
      The singular of "dice."  We seldom hear the word,
      because there is a prohibitory proverb, "Never say
      die."  At long intervals, however, some one says:  "The
      die is cast," which is not true, for it is cut.  The
      word is found in an immortal couplet by that eminent
      poet and domestic economist, Senator Depew:
    </definition>
    <quote>
      <line>A cube of cheese no larger than a die</line>
      <line>May bait the trap to catch a nibbling mie.</line>
    </quote>
  </entry>
</entries>

This data could be expressed in many ways, of course, and some would more closely mimic the structure we established for the HTML or JSON used earlier. Here, however, we're illustrating some of the features of XML designed to make it more readable to humans, such as the use of attributes for term and part rather than tags.

$(document).ready(function() {
  $('#letter-d a').click(function() {
    $.get('d.xml', function(data) {
     
    });
    return false;
  });
});

This time it's the $.get() function that does our work. In general, this function simply fetches the file at the supplied URL and provides the plain text to the callback. However, if the response is known to be XML because of its server-supplied MIME type, the callback will be handed the XML DOM tree.

Fortunately, as we have already seen, jQuery has substantial DOM traversing capabilities. We can use the normal .find(), .filter() and other traversal methods on the XML document just as we would on HTML:

$(document).ready(function() {
  $('#letter-d a').click(function() {
    $.get('d.xml', function(data) {
      $('#dictionary').empty();
      $(data).find('entry').each(function() {
        var $entry = $(this);
        var html = '<div class="entry">';
        html += '<h3 class="term">' + $entry.attr('term')
          + '</h3>';
        html += '<div class="part">' + $entry.attr('part')
          + '</div>';
        html += '<div class="definition">';
        html += $entry.find('definition').text();
        var $quote = $entry.find('quote');
        if ($quote.length) {
          html += '<div class="quote">';
          $quote.find('line').each(function() {
            html += '<div class="quote-line">'
              + $(this).text() + '</div>';
          });
          if ($quote.attr('author')) {
            html += '<div class="quote-author">'
              + $quote.attr('author') + '</div>';
          }
          html += '</div>';
        }
        html += '</div>';
        html += '</div>';
        $('#dictionary').append($(html));
      });
    });
    return false;
  });
});

This has the expected effect when the D link is clicked:

jquery_article2_image06.png

This is a new use for the DOM traversal methods we already know, shedding some light on the flexibility of jQuery's CSS selector support. CSS syntax is typically used to help beautify HTML pages, and thus selectors in standard .css files use HTML tag names such as div and body to locate content. However, jQuery can use arbitrary XML tag names, such as entry and definition here, just as readily as the standard HTML ones.

The advanced selector engine inside jQuery facilitates finding parts of the XML document in much more complicated situations, as well. For example, suppose we wanted to limit the displayed entries to those that have quotes that in turn have attributed authors. To do this, we can limit the entries to those with nested <quote> elements by changing entry to entry:has(quote). Then we can further restrict the entries to those with author attributes on the <quote> elements by writing entry:has(quote[author]). The line with the initial selector now reads:

$(data).find('entry:has(quote[author])').each(function() {

This new selector expression restricts the returned entries correspondingly:

jquery_article2_image07.png




Choosing a data format

We have looked at four formats for our external data, each of which is handled natively by jQuery's AJAX functions. We have also verified that all four can handle the task at hand, loading information onto an existing page when the user requests it and not before. How, then, do we decide which one to use in our applications?

HTML snippets require very little work to implement. The external data can be loaded and inserted into the page with one simple method, which does not even require a callback function. No traversal of the data is necessary for the straightforward task of adding the new HTML into the existing page. On the other hand, the data is not necessarily structured in a way that makes it reusable for other applications. The external file is tightly coupled with its intended container.

JSON files are structured for simple reuse. They are compact, and easy to read. The data structure must be traversed to pull out the information and present it on the page, but this can be done with standard JavaScript techniques. Since the files can be parsed with a single call to JavaScript's eval(), reading in a JSON file is extremely fast. Any use of eval() does carry inherent risks, however. Errors in the JSON file can cause silent failure or even side effects on the page, so the data must be crafted carefully by a trusted party.

JavaScript files offer the ultimate in flexibility, but are not really a data storage mechanism. Since the files are language-specific, they cannot be used to provide the same information to disparate systems. Instead, the ability to load a JavaScript file means that behaviors that are rarely needed can be factored out into external files, reducing code size unless and until it is needed.

XML documents are the kings of portability. Because XML has become the lingua franca of the web service world, providing data in this format makes it very likely the data can be reused elsewhere. For example, Flickr and del.icio.us, export XML representations of their data, which has allowed many interesting mashups of their data to arise. The XML format is somewhat bulky, though, and can be a bit slower to parse and manipulate than other options.

With these characteristics in mind, it is typically easiest to provide external data as HTML snippets, as long as the data is not needed in other applications as well. In cases where the data will be reused but the other applications can also be influenced, JSON is often a good choice due to its performance and size. When the remote application is not known, XML provides the greatest assurance that interoperability will be possible.

More than any other consideration, we should determine if the data is already available. If it is, chances are it's in one of these formats to begin with, so our decision may be made for us.

Passing data to the server

Our examples to this point have focused on the task of retrieving static data files from the web server. However, the AJAX technique really comes into its own only when the server can dynamically shape the data based on input from the browser. We're helped along by jQuery in this task as well; all of the methods we've covered so far can be modified so that data transfer becomes a two-way street.

Since demonstrating these techniques requires interaction with the web server, we'll need to use server-side code for the first time here. The examples given will use the PHP scripting language, which is very widely used as well as freely available. We will not cover how to set up a web server with PHP here; help on this can be found on the websites of Apache or PHP, or from your site's hosting company.

Performing a GET request

To illustrate the communication between client and server, we'll write a script that only sends one dictionary entry to the browser on each request. The entry chosen will depend on a parameter sent from the browser. Our script will pull its data from an internal data structure like this:

<?php
$entries = array(
  'EAVESDROP' => array(
    'part' => 'v.i.',
    'definition' => 'Secretly to overhear a catalogue of the
      crimes and vices of another or yourself.',
    'quote' => array(
      'A lady with one of her ears applied',
      'To an open keyhole heard, inside,',
      'Two female gossips in converse free &mdash;',
      'The subject engaging them was she.',
      '"I think," said one, "and my husband thinks',
      'That she's a prying, inquisitive minx!"',
      'As soon as no more of it she could hear',
      'The lady, indignant, removed her ear.',
      '"I will not stay," she said, with a pout,',
      '"To hear my character lied about!"',
    ),
    'author' => 'Gopete Sherany',
  ),
  'EDIBLE' => array(
    'part' => 'adj.',
    'definition' => 'Good to eat, and wholesome to digest, as
      a worm to a toad, a toad to a snake, a snake to a pig,
      a pig to a man, and a man to a worm.',
  ),
  'EDUCATION' => array(
    'part' => 'n.',
    'definition' => 'That which discloses to the wise and
      disguises from the foolish their lack of
      understanding.',
  ),
);
?>

In a production version of this example, the data would probably be stored in a database and loaded on demand. Since the data is a part of the script here, the code to retrieve it is quite straightforward. We examine the data that has been posted and craft the HTML snippet to display:

<?php
$term = strtoupper($_REQUEST['term']);
if (isset($entries[$term])) {
  $entry = $entries[$term];
 
  $html = '<div class="entry">';
  $html .= '<h3 class="term">';
  $html .= $term;
  $html .= '</h3>';
  $html .= '<div class="part">';
  $html .= $entry['part'];
  $html .= '</div>';
  $html .= '<div class="definition">';
  $html .= $entry['definition'];
  if (isset($entry['quote'])) {
    $html .= '<div class="quote">';
    foreach ($entry['quote'] as $line) {
      $html .= '<div class="quote-line">'. $line .'</div>';
    }
    if (isset($entry['author'])) {
      $html .= '<div class="quote-author">'. $entry['author']
        .'</div>';
    }     
    $html .= '</div>';
  }
  $html .= '</div>';
 
  $html .= '</div>';
 
  print($html);
}
?>

Now requests to this script, which we'll call e.php, will return the HTML snippet corresponding to the term that was sent in the GET parameters. For example, when accessing the script with e.php?term=eavesdrop, we get back:

jquery_article2_image08.png

Once again, we note the lack of formatting we saw with earlier HTML snippets, because CSS rules have not been applied.

Since we're showing how data is passed to the server, we will use a different method to request entries than the solitary buttons we've been relying on so far. Instead, we'll present a list of links for each term, and cause a click on any of them to load the corresponding definition. The HTML we'll add for this looks like:

<div class="letter" id="letter-e">
  <h3>E</h3>
  <ul>
    <li><a href="e.php?term=Eavesdrop">Eavesdrop</a></li>
    <li><a href="e.php?term=Edible">Edible</a></li>
    <li><a href="e.php?term=Education">Education</a></li>
    <li><a href="e.php?term=Eloquence">Eloquence</a></li>
    <li><a href="e.php?term=Elysium">Elysium</a></li>
    <li><a href="e.php?term=Emancipation">Emancipation</a>
      </li>
    <li><a href="e.php?term=Emotion">Emotion</a></li>
    <li><a href="e.php?term=Envelope">Envelope</a></li>
    <li><a href="e.php?term=Envy">Envy</a></li>
    <li><a href="e.php?term=Epitaph">Epitaph</a></li>
    <li><a href="e.php?term=Evangelist">Evangelist</a></li>
  </ul>
</div>

Now we need to get our JavaScript code to call the PHP script with the right parameters. We could do this with the normal .load() mechanism, appending the query string right to the URL and fetching data with addresses like e.php?term=eavesdrop directly.

Instead, though, we can have jQuery construct the query string based on a map we provide to the $.get() function:

$(document).ready(function() {
  $('#letter-e a').click(function() {
    $.get('e.php', {'term': $(this).text()}, function(data) {
      $('#dictionary').html(data);
    });
    return false;
  });
});

Now that we have seen other AJAX interfaces that jQuery provides, the operation of this function seems familiar. The only difference is the second parameter, which allows us to supply a map of keys and values that become part of the query string. In this case, the key is always term but the value is taken from the text of each link. Now, clicking on the first link in the list causes its definition to appear:

jquery_article2_image09.png

All the links here have addresses given, even though we are not using them in the code. This provides an alternative method of navigating the information for users who have JavaScript turned off or unavailable (a form of progressive enhancement). To prevent the links from being followed normally when clicked, the event handler has to return false.

Performing a POST request

HTTP requests using the POST method are almost identical to those using GET. One of the most visible differences is that GET places its arguments in the query string portion of the URL, whereas POST requests do not. However, in AJAX calls, even this distinction is invisible to the average user. Generally, the only reason to choose one method over the other is to conform to the norms of the server-side code, or to provide for large amounts of transmitted data; GET has a more stringent limit. We have coded our PHP example to cope equally well with either method, so we can change from GET to POST simply by changing the jQuery function we call:

$(document).ready(function() {
  $('#letter-e a').click(function() {
    $.post('e.php', {'term': $(this).text()}, function(data) {
      $('#dictionary').html(data);
    });
    return false;
  });
});

The arguments are the same, and the request will now be made via POST. We can further simplify the code by using the .load() method, which uses POST by default when it is supplied with a map of arguments:

$(document).ready(function() {
  $('#letter-e a').click(function() {
    $('#dictionary').load('e.php', {'term': $(this).text()});
    return false;
  });
});

This cut-down version functions the same way when a link is clicked:

jquery_article2_image10.png

Serializing a form

Sending data to the server often involves the user filling out forms. Rather than relying on the normal form submission mechanism, which will load the response in the entire browser window, we can use jQuery's AJAX toolkit to submit the form asynchronously and place the response inside the current page.

To try this out, we'll need to construct a simple form:

<div class="letter" id="letter-f">
  <h3>F</h3>
  <form>
    <input type="text" name="term" value="" id="term" />
    <input type="submit" name="search" value="search"
      id="search" />
  </form>
</div>

This time we'll return a set of entries from the PHP script by searching for the supplied search term as a substring of a dictionary term. The data structure will be of the same format as before, but the logic will be a bit different:

foreach ($entries as $term => $entry) {
  if (strpos($term, strtoupper($_REQUEST['term']))
      !== FALSE) {
    $html = '<div class="entry">';

    $html .= '<h3 class="term">';
    $html .= $term;
    $html .= '</h3>';

    $html .= '<div class="part">';
    $html .= $entry['part'];
    $html .= '</div>';

    $html .= '<div class="definition">';
    $html .= $entry['definition'];
    if (isset($entry['quote'])) {
      foreach ($entry['quote'] as $line) {
        $html .= '<div class="quote-line">'. $line .'</div>';
      }
      if (isset($entry['author'])) {
        $html .= '<div class="quote-author">'.
          $entry['author'] .'</div>';
      }     
    }
    $html .= '</div>';
 
    $html .= '</div>';
 
    print($html);
  }
}

The call to strpos() scans the word for the supplied search string. Now we can react to a form submission and craft the proper query parameters by traversing the DOM tree:

$(document).ready(function() {
  $('#letter-f form').submit(function() {
    $('#dictionary').load('f.php',
      {'term': $('input[name="term"]').val()});
    return false;
  });
});

This code has the intended effect, but searching for input fields by name and appending them to a map one by one is cumbersome. In particular, this approach does not scale well as the form becomes more complex. Fortunately, jQuery offers a shortcut for this often-used idiom. The .serialize() method acts on a jQuery object and translates the matched DOM elements into a query string that can be passed along with an AJAX request. We can generalize our submission handler as follows:

$(document).ready(function() {
  $('#letter-f form').submit(function() {
    $.get('f.php', $(this).serialize(), function(data) {
      $('#dictionary').html(data);
    });
    return false;
  });
});

Now the same script will work to submit the form, even as the number of fields increases. When we perform a search, the matched entries are displayed:

jquery_article2_image11.png

Keeping an eye on the request

So far, it has been sufficient for us to make a call to an AJAX method and patiently await the response. At times, though, it is handy to know a bit more about the HTTP request as it progresses. If such a need arises, jQuery offers a suite of functions that can be used to register callbacks when various AJAX-related events occur.

The .ajaxStart() and .ajaxStop() methods are two examples of these observer functions, and can be attached to any jQuery object. When an AJAX call begins with no other transfer in progress, the .ajaxStart() callback is fired. Conversely, when the last active request ends, the callback attached with .ajaxStop() will be executed. All of the observers are global, in that they are called when any AJAX communication occurs, regardless of what code initiates it.

We can use these methods to provide some feedback to the user in the case of a slow network connection. The HTML for the page can have a suitable loading message appended:

<div id="loading">
  Loading...
</div>

This message is just a piece of arbitrary HTML; it could include an animated GIF image to provide a throbber, for instance. In this case, we'll add a few simple styles to the CSS file, so that when the message is displayed, the page looks like:

jquery_article2_image12.png

In keeping with the spirit of progressive enhancement, however, we won't put this HTML markup directly on the page. It's only relevant for us when JavaScript is available, so we will insert it using jQuery:

$(document).ready(function() {
  $('<div id="loading">Loading...</div>')
    .insertBefore('#dictionary')
});

Our CSS file will give this <div> a display: none; style rule so that the message is initially hidden. To display it at the right time, we just register it as an observer with .ajaxStart():

$(document).ready(function() {
  $('<div id="loading">Loading...</div>')
    .insertBefore('#dictionary')
    .ajaxStart(function() {
      $(this).show();
    });

});

We can chain the hiding behavior right onto this:

$(document).ready(function() {
  $('<div id="loading">Loading...</div>')
    .insertBefore('#dictionary')
    .ajaxStart(function() {
      $(this).show();
    }).ajaxStop(function() {
      $(this).hide();
    });

});

Voilà! We have our loading feedback.

Once again, note that these methods have no association with the particular ways in which the AJAX communications begin. The .load() attached to the A link and the .getJSON() attached to the B link both cause these actions to occur.

In this case, this global behavior is desirable. If we need to get more specific, though, we have a few options at our disposal. Some of the observer methods, like .ajaxError(), send their callback a reference to the XMLHttpRequest object. This can be used to differentiate one request from another, and provide different behaviors. Other more specific handling can be achieved by using the low-level $.ajax() function.

The most common way of interacting with the request, though, is the success callback, which we have already covered. We have used this in several of our examples to interpret the data coming back from the server and to populate the page with the results. It can be used for other feedback too, of course. Consider once again our .load() example:

$(document).ready(function() {
  $('#letter-a a').click(function() {
    $('#dictionary').load('a.html');
    return false;
  });
});

We can create a small enhancement here by making the loaded content fade into view rather than appear suddenly. The .load() can take a callback to be fired on completion:

$(document).ready(function() {
  $('#letter-a a').click(function() {
    $('#dictionary').hide().load('a.html', function() {
      $(this).fadeIn();
    });

    return false;
  });
});

First, we hide the target element, and then initiate the load. When the load is complete, we use the callback to show the newly-populated element by fading it in.

Summary

In the first part of the series we have learned that AJAX methods provided by jQuery can help us to load data in several different formats from the server without a page refresh. We can execute scripts from the server on demand, and send data back to the server.

In the next part we'll learn how to deal with common challenges of asynchronous loading techniques, such as keeping handlers bound after a load has occurred and loading data from a third-party server.

 

This article has been taken from the book Learning jQuery 1.3
Similar Articles