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 2

By Jonathan Chaffer/Karl Swedberg
Posted On Mar 21,2009
Article Rating:
Average Rating: 1
No of Ratings: 1
No of Comments: 0
Category: jQuery
Print this article.

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


In the first part of this two part series we learned that AJAX methods provided by jQuery can help us to load data in several different formats from the server without a page refresh. In this article by Jonathan Chaffer and Karl Swedberg, we will 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.

AJAX and events

Suppose we wanted to allow each dictionary term name to control the display of the definition that follows; clicking on the term name would show or hide the associated definition. With the techniques we have seen so far, this should be pretty straightforward:

$(document).ready(function() {
  $('.term').click(function() {
    $(this).siblings('.definition').slideToggle();
  });
});


When a term is clicked, this code finds siblings of the element that have a class of definition, and slides them up or down as appropriate.

All seems in order, but a click does nothing with this code. Unfortunately, the terms have not yet been added to the document when we attach the click handlers. Even if we managed to attach click handlers to these items, once we clicked on a different letter the handlers would no longer be attached.

This is a common problem with areas of a page populated by AJAX. A popular solution is to rebind handlers each time the page area is refreshed. This can be cumbersome, however, as the event binding code needs to be called each time anything causes the DOM structure of the page to change.

We can implement event delegation, actually binding the event to an ancestor element that never changes. In this case, we'll attach the click handler to the document using .live() and catch our clicks that way:

$(document).ready(function() {
  $('.term').live('click', function() {
    $(this).siblings('.definition').slideToggle();
  });
});


The .live() method tells the browser to observe all clicks anywhere on the page. If (and only if) the clicked element matches the .term selector, then the handler is executed. Now the toggling behavior will take place on any term, even if it is added by a later AJAX transaction.


Security limitations

For all its utility in crafting dynamic web applications, XMLHttpRequest (the underlying browser technology behind jQuery's AJAX implementation) is subject to strict boundaries. To prevent various cross-site scripting attacks, it is not generally possible to request a document from a server other than the one that hosts the original page.

This is generally a positive situation. For example, some cite the implementation of JSON parsing by using eval() as insecure. If malicious code is present in the data file, it could be run by the eval() call. However, since the data file must reside on the same server as the web page itself, the ability to inject code in the data file is largely equivalent to the ability to inject code in the page directly. This means that, for the case of loading trusted JSON files, eval() is not a significant security concern.

There are many cases, though, in which it would be beneficial to load data from a third-party source. There are several ways to work around the security limitations and allow this to happen.

One method is to rely on the server to load the remote data, and then provide it when requested by the client. This is a very powerful approach as the server can perform pre-processing on the data as needed. For example, we could load XML files containing RSS news feeds from several sources, aggregate them into a single feed on the server, and publish this new file for the client when it is requested.

To load data from a remote location without server involvement, we have to get a bit sneakier. A popular approach for the case of loading foreign JavaScript files is injecting <script> tags on demand. Since jQuery can help us insert new DOM elements, it is simple to do this:

$(document.createElement('script'))
  .attr('src', 'http://example.com/example.js')
  .appendTo('head');


In fact, the $.getScript() method will automatically adapt to this technique if it detects a remote host in its URL argument, so even this is handled for us.

The browser will execute the loaded script, but there is no mechanism to retrieve results from the script. For this reason, the technique requires cooperation from the remote host. The loaded script must take some action, such as setting a global variable that has an effect on the local environment. Services that publish scripts that are executable in this way will also provide an API with which to interact with the remote script.

Another option is to use the <iframe> HTML tag to load remote data. This element allows any URL to be used as the source for its data fetching, even if it does not match the host page's server. The data can be loaded and easily displayed on the current page. Manipulating the data, however, typically requires the same cooperation needed for the <script> tag approach; scripts inside the <iframe> need to explicitly provide the data to objects in the parent document.


Using JSONP for remote data

The idea of using <script> tags to fetch JavaScript files from a remote source can be adapted to pull in JSON files from another server as well. To do this, we need to slightly modify the JSON file on the server, however. There are several mechanisms for doing this, one of which is directly supported by jQuery: JSON with Padding, or JSONP.

The JSONP file format consists of a standard JSON file that has been wrapped in parentheses and prepended with an arbitrary text string. This string, the "padding", is determined by the client requesting the data. Because of the parentheses, the client can either cause a function to be called or a variable to be set depending on what is sent as the padding string.

A PHP implementation of the JSONP technique is quite simple:

<?php
  print($_GET['callback'] .'('. $data .')');
?>


Here, $data is a variable containing a string representation of a JSON file. When this script is called, the callback query string parameter is prepended to the resulting file that gets returned to the client.

To demonstrate this technique, we need only slightly modify our earlier JSON example to call this remote data source instead. The $.getJSON() function makes use of a special placeholder character, ?, to achieve this.

$(document).ready(function() {
  var url = 'http://examples.learningjquery.com/jsonp/g.php';
  $('#letter-g a').click(function() {
    $.getJSON(url + '?callback=?', 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;
  });
});


We normally would not be allowed to fetch JSON from a remote server (examples.learningjquery.com in this case). However, since this file is set up to provide its data in the JSONP format, we can obtain the data by appending a query string to our URL, using ? as a placeholder for the value of the callback argument. When the request is made, jQuery replaces the ? for us, parses the result, and passes it to the success function as data just as if this were a local JSON request.

Note that the same security cautions hold here as before; whatever the server decides to return to the browser will execute on the user's computer. The JSONP technique should only be used with data coming from a trusted source.


Additional options

The AJAX toolbox provided by jQuery is well-stocked. We've covered several of the available options, but we've just scratched the surface. While there are too many variants to cover here, we will give an overview of some of the more prominent ways to customize AJAX communications.

The low-level AJAX method

We have seen several methods that all initiate AJAX transactions. Internally, jQuery maps each of these methods onto variants of the $.ajax() global function. Rather than presuming one particular type of AJAX activity, this function takes a map of options that can be used to customize its behavior.

Our first example loaded an HTML snippet using $('#dictionary').load('a.html'). This action could instead be accomplished with $.ajax() as follows:

$.ajax({
  url: 'a.html',
  type: 'GET',
  dataType: 'html',
  success: function(data) {
    $('#dictionary').html(data);
  }
});


We need to explicitly specify the request method, the data type that will be returned, and what to do with the resulting data. Clearly, this is less efficient use of programmer effort; however, with this extra work comes a great deal of flexibility. A few of the special capabilities that come with using a low-level $.ajax() call include:

Ø       Preventing the browser from caching responses from the server. This can be useful if the server produces its data dynamically.

Ø       Registering separate callback functions for when the request completes successfully, with an error, or in all cases.

Ø       Suppressing the global handlers (such as ones registered with $.ajaxStart()) that are normally triggered by all AJAX interactions.

Ø       Providing a user name and password for authentication with the remote host.

For details on using these and other options, consult jQuery Reference Guide or see the API reference online.


Modifying default options

The $.ajaxSetup() function allows us to specify default values for each of the options used when AJAX methods are called. It takes a map of options identical to the ones available to $.ajax() itself, and causes these values to be used on all subsequent AJAX requests unless overridden.

$.ajaxSetup({
  url: 'a.html',
  type: 'POST',
  dataType: 'html'
});
$.ajax({
  type: 'GET',
  success: function(data) {
    $('#dictionary').html(data);
  }
});


This sequence of operations behaves the same as our preceding $.ajax() example. Note that the URL of the request is specified as a default value by the $.ajaxSetup() call, so it does not have to be provided when $.ajax() is invoked. In contrast, the type parameter is given a default value of POST, but this can still be overridden in the $.ajax() call to GET.




Loading parts of an HTML page

The first and simplest AJAX technique we discussed was fetching an HTML snippet and placing it on a page. Sometimes, though, the server already provides the HTML we need, but it is surrounded by an HTML page we do not want. When it is inconvenient to make the server provide the data in the format we desire, jQuery can help us on the client end.

Consider a case like our first example, but in which the document containing the dictionary definitions is a complete HTML page like this:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
    lang="en">
  <head>
    <meta http-equiv="Content-Type"
      content="text/html; charset=utf-8"/>
    <title>The Devil's Dictionary: H</title>
   
    <link rel="stylesheet" href="dictionary.css"
      type="text/css" media="screen" />
  </head>
  <body>
    <div id="container">
      <div id="header">
        <h2>The Devil's Dictionary: H</h2>
        <div class="author">by Ambrose Bierce</div>
      </div>

      <div id="dictionary">
        <div class="entry">
          <h3 class="term">HABEAS CORPUS</h3>
          <div class="part">n.</div>
          <div class="definition">
            A writ by which a man may be taken out of jail
            when confined for the wrong crime.
          </div>
        </div>

        <div class="entry">
          <h3 class="term">HABIT</h3>
          <div class="part">n.</div>
          <div class="definition">
            A shackle for the free.
          </div>
        </div>
      </div>

    </div>
  </body>
</html>


We can load the whole document into our page using the code we wrote earlier:

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


This produces a strange effect, though, due to the pieces of the HTML page we don't want to include:

jquery_article3_image01.png

To remove these extraneous bits, we can use a new feature of the .load() method. When specifying the URL of the document to load, we can also provide a jQuery selector expression. If present, this expression is used to locate a portion of the loaded document. Only the matched part of the document is inserted into the page. In this case, we can use this technique to pull only the dictionary entries from the document and insert them:

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


Now the irrelevant portions of the document are excluded from the page:

jquery_article3_image02.png

Summary

We have thus 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.

We've also learned 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