I recently started looking at Ajax (Asynchronous JavaScript and XML). Initially I wanted to use javascript on the client to look at the headers of web pages, however my mis-understanding soon became apparent in that the client browser running Ajax can only link back to the same server.
Whilst there is aready an Ajax tutorial on 110mb (
http://www.110mb.com/forum/ajax-application-tutorial-t18180.0.html;msg138230#msg138230), I thought I'd add my own experiences if anyone's interested, and specifically, one way to handle concurrent requests.
Briefly, Ajax is a method for the client browser to access information from the server, without having to refresh the browser page.
Firstly, within javascript, you need to create the object for the XML request:
<script>
// For single requests, assign a variable depending on IE / Firefox
if (window.XMLHttpRequest) xmlhttp = XMLHttpRequest(); // Firefox
else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); // IE
// For multiple concurrent requests, this can be done via a function
var xmlhttpTime = initialiseXML(); // create XMLHttpRequest object
var xmlhttpRate = initialiseXML(); // create XMLHttpRequest object
function initialiseXML() {
if (window.XMLHttpRequest) return XMLHttpRequest(); // Firefox
else return new ActiveXObject("Microsoft.XMLHTTP"); // IE
}
</script>
Next, we need to create the request to POST or GET data to the server (there is also an option to extract HEADER information from a web page on the server). For GET, specify the url passing any required variables and specify "true" to execute the request asynchronously (i.e. to run in parallel). For POST, again specify the url however the parameters are passed in the send. Set an event handler to process the returned request, then send the request to the server.
<script>
// GET method
xmlhttp.open("GET","/poc/xmlServerGet.php?return=time",true);
xmlhttp.onreadystatechange = checkData; // function to call when event triggered
xmlhttp.send(null);
// POST method
xmlhttp.open("POST","/poc/xmlServerPost.php",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.onreadystatechange = checkData; // function to call when event triggered
xmlhttp.send("return=time");
</script>
Note that the "checkData" function handles the event and therefore will have the event passed as the parameter. In order to handle concurrent events, this function can be adapted to call a separate function, passing the appropriate XML request object:
<script>
// Call POST or GET function, passing XML request object and server request
xmlRequestPost(xmlhttpTime,'time');
xmlRequestGet(xmlhttpRate,'rate');
function xmlRequestGet(xmlhttp,option) {
xmlhttp.open("GET","/poc/xmlServerGet.php?return="+option,true);
xmlhttp.onreadystatechange = function(){checkData(xmlhttp)};
xmlhttp.send(null);
}
function xmlRequestPost(xmlhttp,option) {
xmlhttp.open("POST","/poc/xmlServerPost.php",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.onreadystatechange = function(){checkData(xmlhttp)};
xmlhttp.send("return="+escape(option));
}
</script>
This request will be passed back to the server, as per the url, which needs to take appropriate action and return data to the javascript client, triggering the event handler. Headers, text, html et cetera can be returned, however XML allows you to return the data in a structured way, for interpretation by the client:
<?php
$return = $_GET['return']; // or $_POST
$parameter = date('H:i:s');
header('Content-Type: text/xml');
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
echo <<<END
<?xml version="1.0" ?>
<response>
<action>$return</action>
<parameter>$parameter</parameter>
</response>
END;
?>
For the single request above, the "checkData" function will be called on an event. For the concurrent requests, it will be called from the event function, passing the XML object variable. The function will be triggered for different events in the request, therefore it needs to interrogate the "readyState" to determine when it is complete (1=Loading 2=Loaded 3=Interactive 4=Complete). The "status" will also be returned corresponding to http status codes (e.g. 301, 404), the normal status is 200.
<script>
// handling single request
function checkData() {
if (xmlhttp.readyState != 4) return; // 1=Loading 2=Loaded 3=Interactive 4=Complete
if (xmlhttp.status != 200) return; // 200=normal 301/302/403/404 et cetera
xmlDoc = xmlhttp.responseXML.documentElement;
if (!xmlDoc) return;
// take any appropriate action required
// (excuse the dodgy newline code, I use 110mb File Manager for maintaining code)
alert("readyState ="+ xmlhttp.readyState +String.fromCharCode(0x0A)+
"status ="+ xmlhttp.status +String.fromCharCode(0x0A)+
"statusText ="+ xmlhttp.statusText +String.fromCharCode(0x0A)+
"responseText ="+ xmlhttp.responseText +String.fromCharCode(0x0A)+
"responseXML ="+ xmlhttp.responseXML );
}
</script>
<script>
// handling concurrent requests
function checkData(xmlhttp) {
if (xmlhttp.readyState != 4) return; // 1=Loading 2=Loaded 3=Interactive 4=Complete
if (xmlhttp.status != 200) return; // 200=normal 301/302/403/404 et cetera
xmlDoc = xmlhttp.responseXML.documentElement;
if (!xmlDoc) return;
// Take appropriate action, depending on returned data
action = xmlDoc.getElementsByTagName("action")[0].childNodes[0].nodeValue
parameter = xmlDoc.getElementsByTagName("parameter")[0].childNodes[0].nodeValue
switch(action) {
case "time": displayTime(parameter); break;
case "rate": displayRate(parameter); break;
default: alert("Invalid function");
}
}
</script>
Putting all of this together, the following example will set up two javascript timers starting off separate functions to request information from the server at specified intervals. These functions will in turn set up the required XML request and handle the returned event. Separate code on the server will pass back the requested information, which is then updated on the screen, highlighted in red.
There is a third timer in the javascript code to monitor how long it's been since the data was last returned and updated.
ajaxClient.php
<html dir="ltr">
<head>
<title>XML Request</title>
</head>
<body>
<h1>XML Request</h1>
<p>Time from server, updated every 15 seconds : <span id="serverTime" style="color: red"><?php echo date("H:i:s") ?></span> (last update <span id="serverTimeInterval">0</span> seconds ago)</p>
<p>Exchange rate from Yahoo, updated every 60 seconds : <span id="dollarPound" style="color: red">$ to £</span> (last update <span id="dollarPoundInterval">0</span> seconds ago)</p>
<script>
var xmlhttpTime = initialiseXML(); // create XMLHttpRequest object
var xmlhttpRate = initialiseXML(); // create XMLHttpRequest object
// timer for Time request every 15 seconds
setInterval("xmlRequestPost(xmlhttpTime,'time')",1000*15);
// timer for Rate request every 60 seconds
setInterval("xmlRequestGet(xmlhttpRate,'rate')",1000*60);
// timer to track last updated
setInterval("increment()",1000);
function initialiseXML() {
if (window.XMLHttpRequest) return XMLHttpRequest(); // Firefox
else return new ActiveXObject("Microsoft.XMLHTTP"); // IE
}
function xmlRequestGet(xmlhttp,option) {
// initialise request with specified url, asynchronously
xmlhttp.open("GET","/poc/xmlServerGet.php?return="+option,true);
// set event handler, will execute on event.type=readystatechange
// With one request, this function can handle the response
// For concurrent requests, need to call a separate function passing the object
xmlhttp.onreadystatechange = function(){checkData(xmlhttp)};
// send HTTP request to the server
xmlhttp.send(null);
}
function xmlRequestPost(xmlhttp,option) {
// initialise request with specified url, asynchronously
xmlhttp.open("POST","/poc/xmlServerPost.php",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// set event handler, will execute on event.type=readystatechange
// With one request, this function can handle the response
// For concurrent requests, need to call a separate function passing the object
xmlhttp.onreadystatechange = function(){checkData(xmlhttp)};
// send HTTP request to the server
xmlhttp.send("return="+escape(option));
}
function checkData(xmlhttp) {
// alert("readyState="+xmlhttp.readyState+String.fromCharCode(0x0A)+
// "status="+xmlhttp.status+String.fromCharCode(0x0A)+
// "statusText="+xmlhttp.statusText+String.fromCharCode(0x0A)+
// "responseText="+xmlhttp.responseText+String.fromCharCode(0x0A)+
// "responseXML="+xmlhttp.responseXML);
if (xmlhttp.readyState != 4) return; // 1=Loading 2=Loaded 3=Interactive 4=Complete
if (xmlhttp.status != 200) return; // 200=normal 301/302/403/404 et cetera
// other variables include xmlhttp.statusText xmlhttp.responseText xmlhttp.responseXML
xmlDoc = xmlhttp.responseXML.documentElement;
if (!xmlDoc) return;
// here, we're only interested in the first occurrence
action = xmlDoc.getElementsByTagName("action")[0].childNodes[0].nodeValue
parameter = xmlDoc.getElementsByTagName("parameter")[0].childNodes[0].nodeValue
switch(action) {
case "time": displayTime(parameter); break;
case "rate": displayRate(parameter); break;
default: alert("Invalid function");
}
}
function displayTime(time) {
document.getElementById('serverTime').innerHTML = time;
document.getElementById('serverTimeInterval').innerHTML = 0;
}
function displayRate(rate) {
document.getElementById('dollarPound').innerHTML = rate;
document.getElementById('dollarPoundInterval').innerHTML = 0;
}
function increment() {
++document.getElementById('serverTimeInterval').innerHTML;
++document.getElementById('dollarPoundInterval').innerHTML;
}
</script>
</body>
</html>
xmlServerGet.php
<?php
$return = $_GET['return'];
switch ($return) {
case "time": $parameter = date('H:i:s'); break;
case "rate":
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl,CURLOPT_URL,"http://download.finance.yahoo.com/d/quotes.csv?s=GBPUSD=X&f=sl1d1t1c1");
$gbpusd = curl_exec($curl);
curl_close($curl);
$dollar = explode(",",$gbpusd);
$parameter = $dollar[1];
break;
default: die('Variable not set');
}
header('Content-Type: text/xml');
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
echo <<<END
<?xml version="1.0" ?>
<response>
<action>$return</action>
<parameter>$parameter</parameter>
</response>
END;
?>
Note: xmlServerPost.php is identical except it uses $_POST.
Example at
http://tpog.110mb.com/poc/ajaxClient.php.
Whilst Ajax has been around for a while, this is my first foray into the code. I would appreciate any comments or feedback. Any questions, please feel free to ask.