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

Exclusive offer: get 50% off this eBook here
Learning jQuery 1.3

Learning jQuery 1.3 — Save 50%

Better Interaction Design and Web Development with Simple JavaScript Techniques

$23.99    $12.00
by Jonathan Chaffer Karl Swedberg | February 2009 | AJAX Open Source Web Development

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.

Learning jQuery 1.3 Better Interaction Design and Web Development with Simple JavaScript Techniques
Published: February 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

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 Better Interaction Design and Web Development with Simple JavaScript Techniques
Published: February 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Jonathan Chaffer

Jonathan Chaffer is a member of Rapid Development Group, a web development firm located in Grand Rapids, Michigan. His work there includes overseeing and implementing projects in a wide variety of technologies, with an emphasis in PHP, MySQL, and JavaScript. He also leads on-site training seminars on the jQuery framework for web developers.

In the open source community, he has been very active in the Drupal CMS project, which has adopted jQuery as its JavaScript framework of choice. He is the creator of the Content Construction Kit, a popular module for managing structured content on Drupal sites. He is also responsible for major overhauls of Drupal's menu system and developer API reference.

He lives in Grand Rapids with his wife, Jennifer.

Karl Swedberg

Karl Swedberg is a web developer at Fusionary Media in Grand Rapids, Michigan, where he spends much of his time making cool things happen with JavaScript. As a member of the jQuery team, he is responsible for maintaining the jQuery API site at api.jquery.com. He is also a member of jQuery's Board of Advisors and a presenter at workshops and conferences. When he isn't coding, he likes to hang out with his family, roast coffee in his garage, and exercise at the local CrossFit gym.

Books From Packt

jQuery UI 1.6: The User Interface Library for jQuery
jQuery UI 1.6: The User Interface Library for jQuery

Spring 2.5 Aspect Oriented Programming
Spring 2.5 Aspect Oriented Programming

Object-Oriented JavaScript
Object-Oriented JavaScript

Drupal 6 Site Builder Solutions
Drupal 6 Site Builder Solutions

RESTful PHP Web Services
RESTful PHP Web services

Practical Plone 3: A Beginner's Guide to Building Powerful Websites
Practical Plone 3: A Beginner's Guide to Building Powerful Websites

 

PHP Web 2.0 Mashup Projects: Practical PHP Mashups with Google Maps, Flickr, Amazon, YouTube, MSN Search, Yahoo!
PHP Web 2.0 Mashup Projects: Practical PHP Mashups with Google Maps, Flickr, Amazon, YouTube, MSN Search, Yahoo!

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
T
4
q
C
a
8
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software