How to Bridge the Client-Server Gap using AJAX (Part II)

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


Learning jQuery 1.3
Learning jQuery 1.3
  • Better Interaction Design and Web Development
    with Simple JavaScript Techniques
  • An introduction to jQuery that requires minimal
    programming experience
  • Detailed solutions to specific client-side
    problems
  • For web designers to create interactive elements
    for their designs
  • For developers to create the best user interface
    for their web applications
  • Packed with great examples, code, and clear
    explanations
  • Revised and updated version of the first book to help you learn
    jQuery

http://www.packtpub.com/learning-jquery-1.3/book


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:

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:

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.


If you have read this article you may be interested to view :


Learning jQuery 1.3
Learning jQuery 1.3
  • Better Interaction Design and Web Development
    with Simple JavaScript Techniques
  • An introduction to jQuery that requires minimal
    programming experience
  • Detailed solutions to specific client-side
    problems
  • For web designers to create interactive elements
    for their designs
  • For developers to create the best user interface
    for their web applications
  • Packed with great examples, code, and clear
    explanations
  • Revised and updated version of the first book to help you learn
    jQuery

http://www.packtpub.com/learning-jquery-1.3/book

Leave a Reply

*