By Shelley Powers
Book Price: $34.99 USD
£24.99 GBP
PDF Price: $27.99
Cover | Table of Contents | Colophon
XMLHttpRequest) for client-server interactionWhile at OSCON, Mark Lucovsky of Google sent us a bit of HTML that'd embed a slender map search widget into our conferences web site. It's an easy way for attendees to find restaurants, hotels, parks, bars, etc. near the conference venue. Great idea, and an elegant demo of the Ajax Search API that Mark's been working on.Mark's next speaking gig was at the Search Engine Strategies (SES) conference, so naturally he reached for the find-me-stuff-around-the-conference example. However, he rapidly ran into the messy HTML that is the SES web site. Whereas it had been a matter of seconds to add the JavaScript into the O'Reilly web page, adding it to the SES page was an ordeal.That's a vindication of the large amount of hard work that [the] O'Reilly design team put into redesigning pages so they were XHTML and CSS. It's also a vindication of the standards themselves: we sometimes lose track of the bigger picture when we're fighting our way through a twisty maze of namespaces, all alike. The point of the standards is not just to ensure that browsers can display the pages. The standards also ensure the pages form a platform that can be built upon; a hacked-together platform leads to brittle and fragile extensions.
div element. In addition, you can't necessarily move a table row or handle a table cell as a distinct object. HTML tables also drive the semantic web people batty—if you use HTML tables to present all of the information on a web page, the tabular data will not stand out.blink. This element caused objects (text, images, etc.) to blink on and off. It has become universally hated and is a poster child for separation of page markup and presentation. Luckily, people only toyed briefly with blink, but another element that was widely used (and is still used) is font.font element gives designers the ability to specify a font family, size, and color:<font size="4" color="blue" face="arial"> Some text </font>
font element lets us make our text more distinctive, but it does have a drawback: if we want to make a change to the font in our web content, we have to hunt down the use of font in all of our web pages and make the change manually. This can make page maintenance excruciating.font (and it has disappeared, more or less, from most pages). If the font element is present in your web sites or applications, you'd be wise to remove it and use a stylesheet setting instead. The following is the CSS setting for the font of a specific element:
#test { font-family: arial; font-size: 4em; color: #0000ff }
h6 to h1. Each HTML list item has a default padding value. Paragraphs are block elements with specific margins and padding, fonts, and line spacing.5x.x2.x8.xx0 - - [31/Aug/2006:03:09:27 +0000] "GET /wp-content/themes/ bbgun/bbgung.png HTTP/1.1" 200 90338 "http://burningbird.net/wp-content/themes/bbgun/ style.css" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6"
x0.xx3.1xx.xx4 - - [31/Aug/2006:03:14:48 +0000] "GET /wp-content/themes/ words/eyesafe.png HTTP/1.1" 404 9058 "http://burningbird.net/" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1b1) Gecko/20060707 Firefox/2.0b1"
XMLHttpRequest to support in-page edits, your use of the new technologies is relatively isolated and will have little impact on the overall site application.XMLHttpRequest.XMLHttpRequest object.XMLHttpRequest object is one of the simpler browser objects to work with: a web service request is created, the request is sent, and a callback function is invoked to process the response. Rather than all of this activity spread across many pages, as is the case with traditional web applications, it's all accomplished within the same web page, without any page reloads.XMLHttpRequest throughout much of the book, but I, and other developers, also sometimes just say XHR to save both time and typing.
<?php
//If no search string is passed, then we can't search
$drink = $_REQUEST['drink'];
if (empty($drink)) {
$drink = 'TEA';
} else {
//Remove whitespace from beginning & end of passed search.
$search = trim($drink);
switch($search) {
case "TEA" :
$result = "<h3>Hot Tea</h3><p>Boil water. " .
"Pour over tea leaves. Steep five minutes. " .
"Strain and serve</p>";
break;
case "APPLETINI" :
$result = "<h3>Appletini</h3><p>Mix 1 oz vodka and 1/2 oz Sour
Apple Pucker or " .
"apple schnapps in a glass filled with ice. Strain into
martini glass. " .
"Garnish with an apple slice or raisin.</p>";
break;
case "NONCHAMP" :
$result = "<h3>Non-Alcoholic Champagne</h3><p>Mix 32 ounces club soda" .
" with 12 ounces frozen white grape juice concentrate.</p>";
break;
case "SWMPMARGARITA" :
$result = "<h3>Swamp Margarita</h3>" .
"<p>Mix 1 1/2 ounce good quality tequila, 3/4 ounce Cointreau, " .
"3/4 ounce Grand Marnier, 1/2 ounce lime juice, and 2
ounces sour mix.
Chill an hour. " .
"Fill bottom of tall glass with several green olives and
a few drops olive juice. " .
"Pour margarita over the olives and let sit for ten minutes. " .
"Strain and serve with a few olives stuffed with pimento on a
toothpick.</p>";
break;
case "LEMON" :
$result = "<h3>Lemon Drop</h3><p>Mix 1 ounce lemon vodka " .
"with 1 ounce lemon juice and 1 teaspoon sugar. Shake with ice, " .
"strain and serve.</p>";
break;
default :
$result = "No recipes found";
break;
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<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>Drinks</title>
<link rel="stylesheet" href="drink.css" type="text/css" media="screen" />
</head>
<body>
<body>
<h1>Drinks</h1>
<div id="drinkblock">
<form action="" id="myform" method="get">
<fieldset legend="drinks">
<label for="drink">Select drink:</label> <select
name="drink" id="drink">
<option value="TEA" <?php if ($drink == 'TEA')
printf('selected="selected"'); ?>>
Hot Tea
</option>
<option
value="APPLETINI" <?php if ($drink == 'APPLETINI')
printf('selected="selected"'); ?>>
Appletini
</option>
<option value="NONCHAMP" <?php if ($drink == 'NONCHAMP')
printf('selected="selected"'); ?>>
Non-alcoholic Champagne
</option>
<option
value="SWMPMARGARITA" <?php if ($drink == 'SWMPMARGARITA')
printf('selected="selected"'); ?>>
Swamp Margarita
</option>
<option
value="LEMON" <?php if ($drink == 'LEMON') printf
('selected="selected"'); ?>>
Lemon Drop
</option>
</select> <input type="submit" value="Get Recipe" />
</fieldset>
</form>
</div>
<div id="drinkblock">
<?php echo $result; ?>
</div>
</body>
</html>
XMLHttpRequest object is whether your application will support IE 6.x. In IE 7, and in newer versions of the other browsers (Opera, Firefox, Safari, Netscape, and so on), the server request object is simply XMLHttpRequest, and the browsers all support similar behavior. However, IE 6.x still supports the older Microsoft ActiveX object. Since older Windows operating systems, such as the very popular Windows 2000, cannot support the newer IE 7, you have to decide whether you will provide support for people using Windows 2000 (or those using Windows XP who have not upgraded their browsers).MSXML2.XMLHttp, MSXML2.XMLHttp.3.0, and MSXML2.XMLHttp.4.0. Most Ajax libraries focus on two of these, the older ID, Microsoft.XMLHttp, and the more common of the newer ones, MSXML2.XMLHttp.XMLHttpRequest object, it's most efficient to test for its presence first. If this test fails, test for the two Microsoft-specific objects. Since we'll be making a great deal of use of XHR, packaging it into a reusable function simplifies its inclusion in a library. shows the code for a typical XMLHttpRequest object function.
function getXmlHttpRequest( ) {
var xmlHttpObj;
if (window.XMLHttpRequest) {
xmlHttpObj = new XMLHttpRequest( );
} else {
try
{
xmlHttpObj = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
xmlHttpObj = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
xmlHttpObj = false;
}
}
}
return xmlHttpObj;
}
GET and POST HTTP requests. XMLHttpRequest supports both of these, and a variety of other verbs. Many of those verbs don't make a lot of sense for Ajax applications, but there are four in particular that are well suited:GETPOSTfunction parameter.DELETEfunction parameter.PUTfunction parameter.GET and POST, are commonly used in web applications, but the latter two may not be familiar to you. All four of these HTTP methods are supported by web services that are considered RESTful—applications based on Representational State Transfer (REST), a recommended approach to providing web services.GET request is one that shouldn't have a side effect—it's used to retrieve information only. Google once came up with beta functionality, the Google Web Accelerator, which promised more efficient applications by automatically prefetching all GETs within a page and caching the data. Unfortunately, some of the getRecipe function makes its request to the same drink.php web application used with the earlier, traditional web application, and the existing server-side application is designed to create a web page rather than to return data. This functionality is apparent when reviewing the data returned in the XHR object's responseText property. Both the page formatting and the data are returned, but an Ajax application needs only the data.echo $result at the end of the recipe.php application to print the result out. With this one small change, the application works whether embedded in a PHP block in a web page or called as a REST service.getDrink function is modified to call the recipe.php web service:
// get recipe using Ajax
function getRecipe(evnt) {
if (!xmlhttp) xmlhttp = aaGetXmlHttpRequest( );
if (!xmlhttp) return;
var drink = encodeURIComponent(document.getElementById('drink').value);
var qry = "drink=" + drink;
var url = 'recipe.php?' + qry;
xmlhttp.open('GET', url, true);
xmlhttp.onreadystatechange = printRecipe;
xmlhttp.send(null);
if (evnt && evnt.preventDefault( ))
evnt.preventDefault( );
return false;
}
XMLHttpRequest has one significant limitation: the same-domain restriction. A request through this object can be made only to the same domain from which the page was first requested, a key part of the JavaScript security sandbox (you can play only in your own "sandbox," thus avoiding the chaos that could result if code could connect anywhere). Among the reasons for this restriction is to ensure that the web page that contains the XHR request can't be used by a malicious site to access resources on a company intranet living behind a firewall. There are ways around this restriction, such as through signed script or setting security parameters in the browsers, but these options are being phased out as new browser versions are released.eval. His parser ensures that there's no hidden scripting embedded in the object that can bite you. Using XML within the DOMParser is actually a safer bet for dynamic scripting. None of these options is going to be completely secure unless you either control the endpoint or web service, or you have a high degree of confidence and trust in a service you're using externally.XMLHttpRequest object doesn't have to be used asynchronously. A synchronous request is one where the browser is locked up waiting for a response. An example of this behavior would be the following:
var state = document.forms[0].elements[0].value;
var url = 'ajax.php?state=' + state;
xmlhttp.open('GET', url, false);
xmlhttp.send(null);
alert(xmlhttp.getAllResponseHeaders( ));
GET request on the web service, ajax.php. The third parameter in the open method is false, which means the request is made synchronously. The request is sent without any parameters (the null value), and immediately after the send, an alert message box is opened and the response headers are printed out.XMLHttpRequest object asynchronously. In this case, an asynchronous request is sent, but the browser continues processing the code following the request rather than waiting for a response from the server. To process the response when it is sent, a function is assigned to the onreadystatechange property of the XMLHttpRequest object. In the function, then, the ready state and request status are tested, and when the ready state is equal to 4, the request is finished.
// get recipe using Ajax
function getRecipe(evnt) {
if (!xmlhttp) xmlhttp = aaGetXmlHttpRequest( );
if (!xmlhttp) return;
var drink = encodeURIComponent(document.getElementById('drink').value);
var qry = "drink=" + drink;
var url = 'recipe.php?' + qry;
xmlhttp.open('GET', url, true);
xmlhttp.onreadystatechange = printRecipe;
xmlhttp.send(null);
if (evnt && evnt.preventDefault( ))
evnt.preventDefault( );
return false;
}
onload event handler, they must be set up correctly or one library will overwrite the other and the setup routine may not be properly executed. These library conflicts can be the worst to debug, too, because if you're not familiar with either library's code, you're also not familiar with how they can compete with each other.
<script type="text/javascript" src="/path/to/prototype.js"></script>
$( ), returns a reference to the element whose name is passed into the function:
var elem = $('some_element');
id attribute:
<div id="some_element">
...
</div>
var elem = document.getElementById('some_element');
$( ) function also appends several Prototype-specific properties.$F( ) returns the value of a form field, given its identifier:
var value = $F('form_field');
...
<input type="text" id="form_field" />
var value = document.getElementById('form_field').value
<script src="javascripts/prototype.js" type="text/javascript"></script> <script src="javascripts/scriptaculous.js" type="text/javascript"></script>
src attribute:<script src="scriptaculous.js?load=effects" type="text/javascript></script>
document.writeln to "add" the libraries. If no library is named, the script.aculo.us loader pulls in all of the libraries.document.write or the DOM to insert new script elements is also called On-Demand JavaScript, a known Ajax pattern, as defined in Michael Mahemoff's book, Ajax Design Patterns (O'Reilly).document.writeln or document.write to add script tags to include new JavaScript libraries is a handy way to dynamically "load" libraries as they're needed. Unfortunately, this doesn't work if you serve the document up as XHTML, since dynamically adding what could be crufty markup into a properly formatted XHTML document isn't allowed. In fact, script.aculo.us doesn't work in a properly formatted XHTML document.fade, and like all Ajax frameworks, it includes functions to simplify Ajax service calls. Rico can be downloaded from http://openrico.org. Rico also has a nicely implemented round corner effect that can be added to page elements. However, whether you want the overhead for this is debatable. I have a feeling that rounded corners will begin to lose some of their popularity—at least until they're available in most browsers as CSS.LiveGrid, which connects to a data source through an Ajax request and serves up new data as the page reader scrolls down the data table. This effect is also known as Ajax pagination or live scrolling.LiveGrid, which supports pagination by working with a live backend database to query the data as needed. The Rico developers provide a demonstration of a LiveGrid Yahoo! search at http://openrico.org/rico/demos.page.dojodojo.langdojo.stringdojo.domdojo.styledojo.htmlDictionary