Important Facts About JavaScript and Related Technologies

By Christian Wenz

Date: Aug 24, 2007

Sample Chapter is provided courtesy of Sams.

Return to the article


This chapter presents not only the most important facts (and phrases) regarding JavaScript, but also related technologies, especially XML handling with JavaScript.

AJAX has generated a huge buzz since the term was coined in February 2005. And although there is a lot of (justified) criticism, regarding both the term and the technology mix it is promising, the whole hype around AJAX led to the rebirth of JavaScript. Not only are underestimated capabilities of JavaScript carried into the daily work of web developers, but more advanced JavaScript features also are en vogue again. This chapter presents not only the most important facts (and phrases) regarding JavaScript, but also related technologies, especially XML handling with JavaScript.

Initializing an AJAX Application

XMLHttp = new XMLHttpRequest();
XMLHttp = new ActiveXObject("Microsoft.XMLHTTP");

The basis of all AJAX applications is the aforementioned XMLHttpRequest object. All AJAX-enabled browsers support it natively, but in Internet Explorer the ActiveX object is required. There is one exception, though: Internet Explorer 7 comes with native XMLHttpRequest support. The best approach to create the object is to use try...catch and to instantiate the native object first (to get Internet Explorer 7 on the right track even though this browser still supports ActiveX), and then the ActiveX version:

if (window.XMLHttpRequest) {
  // instantiate native object
} else if (window.ActiveXObject) {
  // instantiate ActiveX object
}

Regarding the ActiveX object, there are several opportunities to instantiate it. The reason: Microsoft ships various versions of their XML library where this object is hidden. A bulletproof solution would be to check for all versions, using a convoluted piece of code. However, the following approach checks only the most important versions and works on Internet Explorer 5 onward and on all other AJAX-aware browsers:

Creating the XMLHttpRequest Object (xmlhttp.js)

function getXMLHttp() {
  var XMLHttp = null;
  if (window.XMLHttpRequest) {
    try {
      XMLHttp = new XMLHttpRequest();
    } catch (e) { }
  } else if (window.ActiveXObject) {
    try {
      XMLHttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        XMLHttp = new ActiveXObject(
          "Microsoft.XMLHTTP");
      } catch (e) { }
    }
  }
  return XMLHttp;
}

Sending a GET Request

XMLHttp.open("GET", "phrasebook.txt");
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.send(null);

Sending an HTTP request to a server using XMLHttpRequest consists of the following steps:

  1. Provide the URL and the HTTP verb to use.
  2. Define a callback function when results arrive.
  3. Send the request.

Step 1 can be taken with the open() method of the XMLHttpRequest object. This does not—unlike what the method name suggests—actually open an HTTP connection, but just initializes the object. You provide the HTTP verb to use (usually GET or POST) and the URL.

Step 2, the callback function, is provided to the onreadystatechange property of the object. Whenever the readyState property of XMLHttpRequest changes, this callback function is called. Finally, the send() method sends the HTTP request.

In the callback function, the readyState value 4 represents the state of the object we want: call completed. In that case, the responseText property contains the data returned from the server.

Here is a fully working example, sending a GET request to the server (a file called phrasebook.txt with simple text content) and evaluating the response of that call:

Sending a GET Request (xmlhttpget.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
 type="text/javascript">
var XMLHttp = getXMLHttp();
XMLHttp.open("GET", "phrasebook.txt");
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.send(null);

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    window.alert("Returned data: " +
                 XMLHttp.responseText);
  }
}
</script>

Sending a POST Request

XMLHttp.setRequestHeader("Content-type",
  "application/x-www-form-urlencoded");
XMLHttp.send("word1=JavaScript&word2=Phrasebook");

When a GET request is being sent, all parameters are part of the URL. For POST, however, this data is sent in the body of the HTTP request. To do that with the XMLHttpRequest object, the parameters must be provided in the send() method. There is one caveat, though: If you want to access these parameters on the server side, the correct Content-type HTTP header must be set. This is done by using the setRequestHeader() method in the following fashion:

Sending a POST Request (xmlhttppost.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
XMLHttp.open("POST", "post.php");
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.setRequestHeader("Content-type",
  "application/x-www-form-urlencoded");
XMLHttp.send("word1=JavaScript&word2=Phrasebook");

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    window.alert("Returned data: " +
                 XMLHttp.responseText);
  }
}
</script>

The script posts to the URL post.php, which is a simple PHP script just returning the data:

The Target PHP Script of the POST Request (post.php)

<?php
  if (isset($_POST['word1']) &&
      isset($_POST['word2'])) {
    echo $_POST['word1'] . ' ' . $_POST['word2'];
  } else {
    echo 'No data sent.';
  }
?>

Of course, you can provide any other script in any other server-side technology—or you just POST to a plain text file.

Sending a Synchronous Request

XMLHttp.open("GET", "phrasebook.txt", false);

By default, HTTP requests via XMLHttpRequest are asynchronous, which explains the need for a callback function. However, when you set the third parameter of the open() method to false, the request is a synchronous one, which means that the script execution is stopped until data comes back from the server. The following code makes use of that:

Sending a Synchronous Request (xmlhttpsync.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
window.onload = function() {
  var XMLHttp = getXMLHttp();
  XMLHttp.open("GET", "phrasebook.txt", false);
  XMLHttp.send(null);
  document.getElementById("output").innerHTML =
    "Returned data: " + XMLHttp.responseText;
}
</script>
<p id="output">Calling the server ...</p>

Note that the whole code is executed only after the full page has been loaded; otherwise, the access to the output HTML element may fail.

Receiving Multiple Data from the Server

var data = XMLHttp.responseText.split("\n");

By default, the responseText property exists only once, so the whole data returned from the server will be put into the string. However, often it is required that more than one piece of data is returned from the XMLHttpRequest call. There are several solutions for this scenario, but probably the easiest one is to return multiple data and separate the individual data using a separation character that does not occur in the data itself (for instance, a tabulator or a new line or the pipe symbol). Then it is possible to use JavaScript to access this information.

For this phrase, the following server-side text file is queried using HTTP. (In a real-world scenario, there would certainly be a dynamic script on the server side, but for demonstration purposes, the static file is just good enough.)

Multiple Data in One File (phrasebook-multiple.txt)

JavaScript
Phrasebook
Sams Publishing

Then, the following code accesses the individual information in the returned data; Figure 11.1 shows the result.

Working with Multiple Data from the Server (xmlhttpmultiple.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
XMLHttp.open("GET", "phrasebook-multiple.txt");
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.send(null);

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    var data = XMLHttp.responseText.split("\n");
    window.alert(data[0] + " " + data[1] +
                 " by " + data[2]);
  }
}
</script>
Figure 11.1

Figure 11.1 The server sends multiple data.

Aborting an HTTP Request

XMLHttp.abort();

Depending on the browser, only a limited number of HTTP requests can be done at a time. Especially if you have a page with multiple AJAX components (the modern term for this is "mashup"), you may run into trouble. Therefore, aborting an HTTP request may become a necessity.

The method used for that is abort(). The following code aborts the request if it has not been fully executed after five seconds. To demonstrate this behavior, the PHP script delay.php that is called by the code takes 10 seconds to execute. Also note that the readyState property is checked first: if it is 0 or 4, there is nothing to abort.

Aborting an HTTP Request (xmlhttpabort.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
XMLHttp.open("GET", "delay.php?" + Math.random());
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.send(null);
window.setTimeout("timeout();", 5000);

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    window.alert("Returned data: " +
                 XMLHttp.responseText);
  }
}

function timeout() {
  if (XMLHttp.readyState != 4 &&
      XMLHttp.readyState != 0) {
    XMLHttp.onreadystatechange = function() { };
    XMLHttp.abort();
    window.alert("Request aborted");
  }
}
</script>

Five seconds after loading this page, the request is aborted.

Retrieving HTTP Headers

var headers = XMLHttp.getAllResponseHeaders();

The method getAllResponseHeaders() (see preceding code) returns all HTTP headers in the HTTP response, whereas the getResponseHeader() method returns just one specific header. The following code shows how to get information about the type of web server used:

Retrieving an HTTP Response Header (responseheader.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
XMLHttp.open("GET", "phrasebook.txt");
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.send(null);

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    var servertype =
      XMLHttp.getResponseHeader("Server");
    window.alert("Web server used: " + servertype);
  }
}
</script>

Note that not all servers send this header; some also put fake data in it to make server profiling harder.

Receiving XML from the Server

var xml = XMLHttp.responseXML;

The responseText property works well for a limited amount of unstructured data. However, a more elegant approach for using complex, structured data within an AJAX application promises to be the responseXML property. When accessing this property, you get the response of the HTTP request as a JavaScript XML DOM object—of course, only if the server returns valid XML; otherwise, you get null.

Accessing the details of the XML object is quite similar to accessing DOM elements from JavaScript. For this phrase, the following sample XML file will be used:

Sample XML Data (phrasebook.xml)

<books>
  <book pubdate="2006">
    <title>JavaScript Phrasebook</title>
    <publisher>Sams Publishing</publisher>
  <book>
</books>

For the web browsers to read in this XML (Internet Explorer especially is very strict in that regard), the correct MIME type must be sent to the client: text/xml. If you are using Apache, the following configuration in mime.types should do the trick, but is already there by default:

text/xml

On Internet Information Services, you can configure the MIME types in the administration console (Start, Run, inetmgr). Alternatively, let a server-side script serve the file with the correct MIME type; generally this is a must if you are using server-side technology to generate the XML:

Setting the Correct Response MIME Type (phrasebook.xml.php)

<?php
  header('Content-type: text/xml');
  readfile('phrasebook.xml');
?>

Then, the following code accesses the information in the XML file, using the DOM structure and methods like getElementsByTagName() and getAttribute(). Figure 11.2 shows the result.

Extracting Information from the HTTP Response (xmlhttpxml.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
XMLHttp.open("GET", "phrasebook.xml");
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.send(null);

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    var xml = XMLHttp.responseXML;
    var book = xml.getElementsByTagName("book")[0];
    var pubdate = book.getAttribute("pubdate");
    var title, publisher;
    for (var i=0; i<book.childNodes.length; i++) {
      if (book.childNodes[i].nodeName == "title") {
        title = book.childNodes[i].firstChild.nodeValue;
      } else if (book.childNodes[i].nodeName == "publisher") {
        publisher = book.childNodes[i].firstChild.nodeValue;
      }
    }
    window.alert(title + " by " + publisher +
                 " (" + pubdate + ")");
  }
}
</script>
Figure 11.2

Figure 11.2 The data from the XML file.

Using JSON for Data (De)Serialization

var json = XMLHttp.responseText;
var book = eval("(" + json + ")");;

JSON is becoming more and more the de facto standard data exchange format for AJAX applications. Many AJAX frameworks support JSON, many Web Services provide a JSON interface, and PHP 6 will most probably feature JSON support at the core of the language.

Using JSON within JavaScript is quite simple, as well. The preceding code evaluates JSON and converts it into a JavaScript object—a simple eval() function call does the trick.

The JSON notation from the previous sidebar, "Understanding JSON," is stored in a file called phrasebook.json; then, the following code reads in this file using XMLHttpRequest and then outputs some data from it:

Using JSON for Data Deserialization (xmlhttpjson.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
XMLHttp.open("GET", "phrasebook.json");
XMLHttp.onreadystatechange = handlerFunction;
XMLHttp.send(null);

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    var json = XMLHttp.responseText;
    var book = eval("(" + json + ")");
    var pubdate = book.pubdate;
    var title = book.title;
    var publisher = book.publisher;
    window.alert(title + " by " + publisher +
                 " (" + pubdate + ")");
  }
}
</script>

Creating a Waiting Screen

document.getElementById("loading").style.visibility = "hidden";

One of the largest obstacles for modern web applications is latency: Something happens, but in the background. You do have to inform the users; otherwise, they will not notice that something is coming up. One way of doing this is by changing the mouse cursor (see Chapter 4, "CSS"); another way is by using a waiting banner. Many applications let a banner labeled with "waiting" or "loading" fade in when an XMLHttpRequest call is executed; one of the first websites to use this was Google Mail.

Actually, this phrase requires more DHTML than AJAX. When the (asynchronous!) call is sent to the server, the loading screen is shown and positioned in the upper-right corner (you can, of course, use any arbitrary position of your liking). After data comes back from the server, the banner is made invisible again. The following code implements this in a browser-agnostic fashion:

Implementing a Waiting Screen (waiting.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
window.onload = function() {
  XMLHttp.open("GET", "delay.php?" + Math.random());
  XMLHttp.onreadystatechange = handlerFunction;
  XMLHttp.send(null);
  with (document.getElementById("loading")) {
    style.visibility = "visible";
    if (navigator.appName ==
      "Microsoft Internet Explorer") {
      style.posLeft =
        document.body.clientWidth - 75;
      style.posTop = 0;
    } else {
      style.left = (window.innerWidth - 75) + "px";
      style.top = "0px";
    }
  }
}

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    document.getElementById("loading").style.visibility = "hidden";
    window.alert("Returned data: " +
                 XMLHttp.responseText);
  }
}
</script>
<span id="loading" style="position: absolute; visibility: hidden; background-color: red; width: 75px;">Loading ...</span>

Figure 11.3 shows the browser while waiting for results: The banner appears (and vanishes again after the data from the HTTP request has arrived).

Figure 11.3

Figure 11.3 The waiting screen.

Solving the Bookmark Problem

var data = unescape(location.hash.substring(1));

One of the main issues with AJAX applications today is that it is not possible to bookmark a page. Since the contents of a page can change thanks to XMLHttpRequest calls, but the URL stays the same, bookmarking does not work.

There is, however, a workaround. Caveat: This workaround is just the shell of the code you have to write; the actual work you have to do (depending on your application) is how to persist data and then apply it back to the page.

The trick is to append data that identifies the current state of the page to the URL. Not in the form of a GET parameter (since this would cause a reload of the page), but in the hash:

http://server/file.html#info

The data denoted here with info must identify the current page status. The implementation details largely depend on how AJAX is used on the page.

Whenever the current state of the page changes, the page's hash (location.hash in JavaScript) must be updated. Then, the following code snippet reads in this information after the page is loaded. You have to implement the applyData() function that takes care of transforming the information in location.hash into actual content on the page.

window.onload = function() {
  if (location.hash.length >= 2) {
    var data = unescape(location.hash.substring(1));
    applyData(data);
  }
};

This, of course, increases the amount of work to be done, but your users benefit greatly from this convenient feature. Unfortunately, this approach does not work (yet) with Safari and Konqueror since they handle location.hash in a different way.

Solving the Back Button Problem

window.frames["loader"].window.location.search =  
  "?" + escape(data);

A problem related to the issue tackled in the previous phrase is the back button (and, of course, the forward button as well). When the page's URL does not change but its contents do, the back button does not really work as expected.

To solve this problem—and again this phrase can show only a generic approach, not a full solution, since there are so many implementation-dependent details—two subproblems have to be worked on:

The solution to the second subproblem is to use a hidden iframe that will load an invisible page, just to get an entry in the history of the page. Here is the iframe:

<iframe src="loader.html" name="loader"
  style="display:none"></iframe>

Whenever something happens on the page (AJAX-wise), the loader frame must be forced to load again, with data appended to the URL (unlike the previous code, this time there must be a reload):

if (window.ActiveXObject) {
  window.frames["loader"].window.location.search =
    "?" + escape(data);
}

Finally, the loader frame must take care of applying any loaded data to the main document; at least for Internet Explorer this is required, since in this browser the back and forward buttons change the contents of the iframe!

window.onload = function() {
  if (location.search.length >= 2) {
    var data = unescape(
      location.search.substring(1));
    top.applyData(data);
  }
};

This makes the coder's life much more complicated, especially if there are many different AJAX effects on the page. But just to repeat one of the final sentences from the previous phrase: Your users benefit greatly from this convenient feature. And again, this workaround does not work with Safari and Konqueror (yet).

Using XSLT

var process = new XSLTProcessor();

Both Internet Explorer and Mozilla browsers do support XSLT from JavaScript. However, the approach is very different. For Microsoft Internet Explorer, once again ActiveX is required (also with Internet Explorer 7). You first load the XSLT file, then the XML (coming from XMLHttpRequest, of course). Finally, the result of the XSL transformation is available as a string and can, for instance, be appended to the page.

Mozilla browsers, on the other hand, use a different approach; they rely exclusively on native object. The result, however, is different from the Internet Explorer implementation. At the end, you get a DOM fragment, which you can append to the current page's DOM.

The following code covers both major browser types:

Using XSLT with JavaScript (xmlhttpxsl.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script language="JavaScript"
  type="text/javascript">
var XMLHttp = getXMLHttp();
window.onload = function() {
  XMLHttp.open("GET", "phrasebooks.xml");
  XMLHttp.onreadystatechange = handlerFunction;
  XMLHttp.send(null);
}

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    var xml = XMLHttp.responseXML;
    if (window.ActiveXObject) {
      var xslt = new ActiveXObject(
        "MSXML2.FreeThreadedDOMDocument");
      xslt.async = false;
      xslt.load("phrasebooks.xsl");
      var template = new ActiveXObject(
        "MSXML2.XSLTemplate");
      template.stylesheet = xslt;
      var process = template.createProcessor();
      process.input = xml;
      process.transform();
      var para = document.createElement("p");
      para.innerHTML = process.output;
      document.body.appendChild(para);
    } else if (window.XSLTProcessor) {
      var xslt = document.implementation.createDocument("", "", null);
      xslt.async = false;
      xslt.load("phrasebooks.xsl");
      var process = new XSLTProcessor();
      process.importStylesheet(xslt);
      var result = process.transformToFragment(
        xml, document);
      document.body.appendChild(result);
    }
  }
}
</script>

Figure 11.4 shows the output of the transformation: The data in the XML file is presented as a bulleted list.

Figure 11.4

Figure 11.4 The outcome of the XSL transformation.

As Figure 11.4 also demonstrates, the Opera browser (from version 9 onward) has a compatible XSLT implementation, using the Mozilla approach.

Using an XML Library

var result = xsltProcess(xml, xslt);

As you can see, using XSLT from JavaScript can be quite tricky if it is to work on as many browsers as possible. In this case, using an external framework can really save a lot of time and effort.

Google has created such a framework ("AJAXSLT") which is available free (under a BSD license) at http://goog-ajaxslt.sourceforge.net/. The following phrase uses version 0.4 of the framework, which you can also see from the paths used in the code. The framework itself is also not part of the source code downloads for this book.

The following phrase does the XSLT transformation, but this time relies on the functionality offered by AJAXSLT.

Using XSLT with JavaScript and AJAXSLT (ajaxslt.html)

<script language="JavaScript"
  type="text/javascript" src="xmlhttp.js"></script>
<script src="ajaxslt-0.4/misc.js"
  type="text/javascript"></script>
<script src="ajaxslt-0.4/dom.js"
  type="text/javascript"></script>
<script src="ajaxslt-0.4/xpath.js"
  type="text/javascript"></script>
<script src="ajaxslt-0.4/xslt.js"
  type="text/javascript"></script>
<script language="JavaScript"
  type="text/javascript">
var xml = null;
var xslt = null;
var XMLHttp1 = getXMLHttp();
var XMLHttp2 = getXMLHttp();
window.onload = function() {
  XMLHttp1.open("GET", "phrasebooks.xml");
  XMLHttp1.onreadystatechange = handlerFunction1;
  XMLHttp1.send(null);
  XMLHttp2.open("GET", "phrasebooks.xsl");
  XMLHttp2.onreadystatechange = handlerFunction2;
  XMLHttp2.send(null);
}

function handlerFunction1() {
  if (XMLHttp1.readyState == 4) {
    xml = xmlParse(XMLHttp1.responseText);
    if (xslt != null) {
      transform();
    }
  }
}

function handlerFunction2() {
  if (XMLHttp2.readyState == 4) {
    xslt = xmlParse(XMLHttp2.responseText);
    if (xml != null) {
      transform();
    }
  }
}

function transform() {
  var result = xsltProcess(xml, xslt);
  document.getElementById("output").innerHTML =
    result;
}
</script>
<div id="output"></div>

This not only works fine (as you can see, most of the code is "spent" for loading the XML and XSL files), but also works cross-browser, as Figure 11.5 (shot in Konqueror) demonstrates. Another nice feature: You can also use XPath with this library. Both Internet Explorer and Mozilla browsers and Opera work with XPath, but in an even more incompatible way than they do regarding XSLT. Also, the AJAXSLT XPath support can deal with other browsers, as well.

Figure 11.5

Figure 11.5 With AJAXLST the transformation even works in Konqueror.

Using the Yahoo! Web Service

<script language="JavaScript" type="text/javascript"
  src="http://api.search.yahoo.com/WebSearchService/
V1/webSearch?appid=XXXXX&query=JavaScript&output=
json&callback=showResults">
</script>

More and more Web Services provide a JSON interface, and among the first ones to do so were the Yahoo! Web Services. After a (free) registration at http://api.search.yahoo.com/webservices/register_application, you get your personal application ID. This ID is tied to your application and must be used instead of the XXXXX placeholder in this phrase. Then, the preceding <script> tag not only calls the Web Service and expects JSON back, but also provides the name of a callback function that is called after the data is there. So your application receives JavaScript code from the Yahoo! server and executes it—which means you have to trust Yahoo! in order to use it. Then, the callback function gets a JavaScript object with the Yahoo! search results.

The following code then creates a bulleted list with the data from the Web Service. More information about the specific format of the data returned from Yahoo! can be found at the online documentation at http://developer.yahoo.com/search/web/V1/webSearch.html. Figure 11.6 shows the output of this code.

Calling the Yahoo! Web Service with JavaScript (yahoowebservice.html)

<script language="JavaScript"
  type="text/javascript">
function showResults(data) {
  var ul = document.getElementById("output");
  for (var i=0; i < data.ResultSet.Result.length; i++) {
    var text = document.createTextNode(
      data.ResultSet.Result[i].Title + " - " +
      data.ResultSet.Result[i].Url);
    var li = document.createElement("li");
    li.appendChild(text);
    ul.appendChild(li);
  }
}
</script>
<body>
  <p><ul id="output"></ul></p>
</body>
<script language="JavaScript" type="text/javascript"
  src="http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=XXXXX&query=JavaScript&output=json&callback=showResults">
</script>
Figure 11.6

Figure 11.6 The Yahoo! search results.