Tải bản đầy đủ (.pdf) (10 trang)

Publishing AJAX and PHP - part 6 ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.48 MB, 10 trang )

Client-Side Techniques with Smarter JavaScript

50
// (use xmlHttp.responseXML to read an XML response as a DOM object)
// do something with the response
//
//
}
}
}
Once again we can successfully use try/catch to handle errors that could happen while initiating a
connection to the server, or while reading the response from the server.
A safer version of the
handleRequestStateChange method looks like this:
// function executed when the state of the request changes
function handleRequestStateChange()
{
// continue if the process is completed
if (xmlHttp.readyState == 4)
{
// continue only if HTTP status is "OK"
if (xmlHttp.status == 200)
{
try
{
// retrieve the response
response = xmlHttp.responseText;
// do something with the response
//
//
}


catch(e)
{
// display error message
alert("Error reading the response: " + e.toString());
}
}
else
{
// display status message
alert("There was a problem retrieving the data:\n" +
xmlHttp.statusText);
}
}
}
OK, let's see how these functions work in action.
Time for Action—Making Asynchronous Calls with XMLHttpRequest
1. In the foundations folder, create a subfolder named async.
2. In the
async folder, create a file called async.txt, and add the following text to it:
Hello client!
3. In the same folder create a file called async.html, and add the following code to it:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"
<html>
<head>
<title>AJAX Foundations: Using XMLHttpRequest</title>
<script type="text/javascript" src="async.js"></script>
</head>
<body onload="process()">
Hello, server!

Chapter 2
<br/>
<div id="myDivElement" />
</body>
</html>
4. Create a file called async.js with the following contents:
// holds an instance of XMLHttpRequest
var xmlHttp = createXmlHttpRequestObject();

// creates an XMLHttpRequest instance
function createXmlHttpRequestObject()
{
// will store the reference to the XMLHttpRequest object
var xmlHttp;
// this should work for all browsers except IE6 and older
try
{
// try to create XMLHttpRequest object
xmlHttp = new XMLHttpRequest();
}
catch(e)
{
// assume IE6 or older
var XmlHttpVersions = new Array("MSXML2.XMLHTTP.6.0",
"MSXML2.XMLHTTP.5.0",
"MSXML2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0",
"MSXML2.XMLHTTP",
"Microsoft.XMLHTTP");
// try every prog id until one works

for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++)
{
try
{
// try to create XMLHttpRequest object
xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
}
catch (e) {}
}
}
// return the created object or display an error message
if (!xmlHttp)
alert("Error creating the XMLHttpRequest object.");
else
return xmlHttp;
}


// called to read a file from the server
function process()
{
// only continue if xmlHttp isn't void
if (xmlHttp)
{
// try to connect to the server
try
{
// initiate reading the async.txt file from the server
xmlHttp.open("GET", "async.txt", true);
xmlHttp.onreadystatechange = handleRequestStateChange;

xmlHttp.send(null);
}
// display the error in case of failure
catch (e)

51
Client-Side Techniques with Smarter JavaScript

52
{
alert("Can't connect to server:\n" + e.toString());
}
}
}

// function that handles the HTTP response
function handleRequestStateChange()
{
// obtain a reference to the <div> element on the page
myDiv = document.getElementById("myDivElement");
// display the status of the request
if (xmlHttp.readyState == 1)
{
myDiv.innerHTML += "Request status: 1 (loading) <br/>";
}
else if (xmlHttp.readyState == 2)
{
myDiv.innerHTML += "Request status: 2 (loaded) <br/>";
}
else if (xmlHttp.readyState == 3)

{
myDiv.innerHTML += "Request status: 3 (interactive) <br/>";
}
// when readyState is 4, we also read the server response
else if (xmlHttp.readyState == 4)
{
// continue only if HTTP status is "OK"
if (xmlHttp.status == 200)
{
try
{
// read the message from the server
response = xmlHttp.responseText;
// display the message
myDiv.innerHTML +=
"Request status: 4 (complete). Server said: <br/>";
myDiv.innerHTML += response;
}
catch(e)
{
// display error message
alert("Error reading the response: " + e.toString());
}
}
else
{
// display status message
alert("There was a problem retrieving the data:\n" +
xmlHttp.statusText);
}

}
}
5. Load the async.html file through the HTTP server by loading http://localhost/
ajax/foundations/async/async.html
in your browser (you must load it through
HTTP; local access won't work this time). Expect to see the results similar to those
shown in Figure 2.6:
Chapter 2


Figure 2.6: The Four HTTP Request Status Codes
Don't worry if your browser doesn't display exactly the same message. Some
XMLHttpRequest implementations simply ignore some status codes. Opera, for example,
will only fire the event for status codes 3 and 4. Internet Explorer will report status codes
2, 3, and 4 when using a more recent XMLHttp version.
What Just Happened?
To understand the exact flow of execution, let's start from where the processing begins—the
async.html file:
<html>
<head>
<title>AJAX Foundations: Using XMLHttpRequest</title>
<script type="text/javascript"
src="async.js"></script>
</head>
<body
onload="process()">
This bit of code hides some interesting functionality. First, it references the async.js file, the
moment at which the code in that file is parsed. Note that the code residing in JavaScript functions
does not execute automatically, but the rest of the code does. All the code in our JavaScript file is
packaged as functions, except one line:

// holds an instance of XMLHttpRequest
var xmlHttp = createXmlHttpRequestObject();
This way we ensure that the xmlHttp variable contains an XMLHttpRequest instance right from the
start. The
XMLHttpRequest instance is created by calling the createXmlHttpRequestObject
function that you encountered a bit earlier.
The
process() method gets executed when the onload event fires. The process() method can
rely on the
xmlHttp object being already initialized, so it only focuses on initializing a server
request. The proper error-handling sequence is used to guard against potential problems. The code
that initiates the server request is:
// initiate reading the async.txt file from the server
xmlHttp.open("GET", "async.txt", true);

53
Client-Side Techniques with Smarter JavaScript

54
xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send(null);
Note that you cannot load the script locally, directly from the disk using a file://
resource. Instead, you need to load it through HTTP. To load it locally, you would need
to mention the complete access path to the .txt file, and in that case you may meet a
security problem that we will deal with later.
Supposing that the HTTP request was successfully initialized and executed asynchronously, the
handleRequestStateChange method will get called every time the state of the request changes. In
real applications we will ignore all states except
4 (which signals the request has completed), but
in this exercise we print a message with each state so you can see the callback method actually

gets executed as advertised.
The code in
handleRequestStateChange is not that exciting by itself, but the fact that it's being
called for you is very nice indeed. Instead of waiting for the server to reply with a synchronous
HTTP call, making the request asynchronously allows you to continue doing other tasks until a
response is received.
The
handleRequestStateChange function starts by obtaining a reference to the HTML element
called
myDivElement, which is used to display the various states the HTTP request is going through:
// function that handles the HTTP response
function handleRequestStateChange()
{
// obtain a reference to the <div> element on the page
myDiv = document.getElementById("myDivElement");
// display the status o the request
if (xmlHttp.readyState == 1)
{
myDiv.innerHTML += "Request status: 1 (loading) <br/>";
}
else if (xmlHttp.readyState == 2)


When the status hits the value of 4, we have the typical code that deals with reading the server
response, hidden inside
xmlHttp.ResponseText:
// when readyState is 4, we also read the server response
else if (xmlHttp.readyState == 4)
{
// continue only if HTTP status is "OK"

if (xmlHttp.status == 200)
{
try
{
// read the message from the server
response = xmlHttp.responseText;
// display the message
myDiv.innerHTML += "Request status: 4 (complete). Server said: <br/>";
myDiv.innerHTML += response;
}
catch(e)
{
// display error message
alert("Error reading the response: " + e.toString());
}
Chapter 2
}
else
{
// display status message
alert("There was a problem retrieving the data:\n" +
xmlHttp.statusText);
}
}
Apart from the error-handling bits, it's good to notice the xmlHttp.responseText method that
reads the response from the server. This method has a bigger brother called
xmlHttp.responseXml, which can be used when the response from the server is in XML format.
Unless the responseXml method of the XMLHttpRequest object is used, there's really no
XML appearing anywhere, except for the name of that object (the exercise you have just
completed is a perfect example of this). A better name for the object would have been

"HttpRequest". The XML prefix was probably added by Microsoft because it sounded
good at that moment, when XML was a big buzzword as AJAX is nowadays. Don't be
surprised if you will see objects called AjaxRequest (or similar) in the days to come.
Working with XML Structures
XML documents are similar to HTML documents in that they are text-based, and contain
hierarchies of elements. In the last few years, XML has become very popular for packaging and
delivering all kinds of data.
Incidentally, XML puts the X in AJAX, and the prefix in
XMLHttpRequest. However, once again,
note that using XML is optional. In the previous exercise, you created a simple application that
made an asynchronous call to the server, just to receive a text document; no XML was involved.
XML is a vast subject, with many complementary technologies. You will hear people
talking about DTDs, schemas and namespaces, XSLT and XPath, XLink and XPointer,
and more. In this book we will mostly use XML for transmitting simple structures of
data. For a quick-start introduction to XML we recommend
docs/xml-basics.html. If you don't mind the ads,
xml/default.asp is a good resource as well. Appendix C available at
contains an introduction to XSLT and Xpath.
You can use the DOM to manipulate XML files just as you did for manipulating HTML files. The
following exercise is similar to the previous exercise in that you read a static file from the server.
The novelty is that the file is XML, and we read it using the DOM.
Time for Action—Making Asynchronous Calls with XMLHttpRequest
and XML
1. In the foundations folder create a subfolder called xml.
2. In the xml folder, create a file called books.xml, which will contain the XML structure
that we will read using JavaScript's DOM. Add the following content to the file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

55
Client-Side Techniques with Smarter JavaScript


56
<response>
<books>
<book>
<title>
Building Reponsive Web Applications with AJAX and PHP
</title>
<isbn>
1-904811-82-5
</isbn>
</book>
<book>
<title>
Beginning PHP 5 and MySQL E-Commerce: From Novice to Professional
</title>
<isbn>
1-59059-392-8
</isbn>
</book>
</books>
</response>
3. In the same folder create a file called books.html, and add the following code to it:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"
<html>
<head>
<title>AJAX Foundations: JavaScript and XML</title>
<script type="text/javascript" src="books.js"></script>
</head>

<body onload="process()">
Server, tell me your favorite books!
<br/>
<div id="myDivElement" />
</body>
</html>
4. Finally, create the books.js file:
// holds an instance of XMLHttpRequest
var xmlHttp = createXmlHttpRequestObject();

// creates an XMLHttpRequest instance
function createXmlHttpRequestObject()
{
// will store the reference to the XMLHttpRequest object
var xmlHttp;
// this should work for all browsers except IE6 and older
try
{
// try to create XMLHttpRequest object
xmlHttp = new XMLHttpRequest();
}
catch(e)
{
// assume IE6 or older
var XmlHttpVersions = new Array('MSXML2.XMLHTTP.6.0',
'MSXML2.XMLHTTP.5.0',
'MSXML2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP');

// try every prog id until one works
for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++)
{
try
Chapter 2
{
// try to create XMLHttpRequest object
xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
}
catch (e) {}
}
}
// return the created object or display an error message
if (!xmlHttp)
alert("Error creating the XMLHttpRequest object.");
else
return xmlHttp;
}

// read a file from the server
function process()
{
// only continue if xmlHttp isn't void
if (xmlHttp)
{
// try to connect to the server
try
{
// initiate reading a file from the server
xmlHttp.open("GET", "books.xml", true);

xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send(null);
}
// display the error in case of failure
catch (e)
{
alert("Can't connect to server:\n" + e.toString());
}
}
}

// function called when the state of the HTTP request changes
function handleRequestStateChange()
{
// when readyState is 4, we are ready to read the server response
if (xmlHttp.readyState == 4)
{
// continue only if HTTP status is "OK"
if (xmlHttp.status == 200)
{
try
{
// do something with the response from the server
handleServerResponse();
}
catch(e)
{
// display error message
alert("Error reading the response: " + e.toString());
}

}
else
{
// display status message
alert("There was a problem retrieving the data:\n" +
xmlHttp.statusText);
}
}
}


57
Client-Side Techniques with Smarter JavaScript

58
// handles the response received from the server
function handleServerResponse()
{
// read the message from the server
var xmlResponse = xmlHttp.responseXML;
// obtain the XML's document element
xmlRoot = xmlResponse.documentElement;
// obtain arrays with book titles and ISBNs
titleArray = xmlRoot.getElementsByTagName("title");
isbnArray = xmlRoot.getElementsByTagName("isbn");
// generate HTML output
var html = "";
// iterate through the arrays and create an HTML structure
for (var i=0; i<titleArray.length; i++)
html += titleArray.item(i).firstChild.data +

", " + isbnArray.item(i).firstChild.data + "<br/>";
// obtain a reference to the <div> element on the page
myDiv = document.getElementById("myDivElement");
// display the HTML output
myDiv.innerHTML = "Server says: <br />" + html;
}
5. Load http://localhost/ajax/foundations/xml/books.html:

Figure 2.7: The Server Knows What It's Talking About
What Just Happened?
Most of the code will already start looking familiar, as it builds the basic framework we have built
so far. The novelty consists in the
handleServerResponse function, which is called from
handleRequestStateChange when the request is complete.
The handleServerResponse function starts by retrieving the server response in XML format:
// handles the response received from the server
function handleServerResponse()
{
// read the message from the server
var xmlResponse = xmlHttp.responseXML;
Chapter 2
The responseXML method of the XMLHttpRequest object wraps the received response as a DOM
document. If the response isn't a valid XML document, the browser might throw an error.
However this depends on the specific browser you're using, because each JavaScript and DOM
implementation behaves in its own way.
We will get back to bulletproofing the XML reading code in a minute; for now, let us assume the
XML document is valid, and let's see how we read it. As you know, an XML document must have
one (and only one)
document element, which is the root element. In our case this is <response>.
You will usually need a reference to the document element to start with, as we did in our exercise:

// obtain the XML's document element
xmlRoot = xmlResponse.documentElement;
The next step was to create two arrays, one with book titles and one with book ISBNs. We did that
using the
getElementsByTagName DOM function, which parses the entire XML file and retrieves
the elements with the specified name:
// obtain arrays with book titles and ISBNs
titleArray = xmlRoot.getElementsByTagName("title");
isbnArray = xmlRoot.getElementsByTagName("isbn");
This is, of course, one of the many ways in which you can read an XML file using the DOM. A
much more powerful way is to use XPath, which allows you to define powerful queries on your
XML document. .
The two arrays that we generated are arrays of DOM elements. In our case, the text that we want
displayed is the first child element of the
title and isbn elements (the first child element is the
text element that contains the data we want to display).
// generate HTML output
var html = "";
// iterate through the arrays and create an HTML structure
for (var i=0; i<titleArray.length; i++)
html +=
titleArray.item(i).firstChild.data +
", " + isbnArray.item(i).firstChild.data + "<br/>";
// obtain a reference to the <div> element on the page
myDiv = document.getElementById('myDivElement');
// display the HTML output
myDiv.innerHTML = "Server says: <br />" + html;
}
The highlighted bits are used to build an HTML structure that is inserted into the page using the
div element that is defined in books.html.

Handling More Errors and Throwing Exceptions
As highlighted earlier, if the XML document you're trying to read is not valid, each browser reacts
in its own way. We have made a simple test by removing the closing
</response> tag from
books.xml. Firefox will throw an error to the JavaScript console, but besides that, no error will be
shown to the user. This is not good, of course, because not many users browse websites looking at
the JavaScript console.

59

×