JavaScript: Web Services using XMLHttpRequest (Ajax)
If you're looking for a script that can handle concurrent XMLHttpRequests then you should look at our AjaxRequest class which is based on the code presented here, but set up using a class.
XMLHttpRequest (also known as 'Ajax') was definately one of the 'big things' of 2005 with major leaps forward by Google (Google Mail, Google Maps) and other online services (Flickr) making their sites not just functional but enjoyable to use.
Originally XMLHttpRequest was an ActiveX object meaning that it's use was restricted to Microsoft-based intranets and not really suitable for websites. Fortunately the Mozilla team was interested enough to implement a JavaScript equivalent which has now spread not just to all Mozilla-based browsers but also to Gecko/KHTML-based browsers and Opera. Even IE7 no longer requires ActiveX to be installed to make use of XMLHttpRequest.
The JavaScript code we've developed comes in two parts. First we instantiate the XMLHttpRequest object and then process the response values. Also on this page you'll find some working examples and details on how to avoid the caching problem in Internet Explorer.
Creating an XMLHttpRequest object
Initialising the XMLHttpRequest object is actually quite simple in most browsers, but to support MSIE 5 and 6 we need to cater for a number of different methods which is why it looks so complicated. Basically the script tries three different methods in sequence until it's successful or runs out of options:
var req; // global variable to hold request object
function loadXMLDoc(url, params)
{
if(window.XMLHttpRequest) {
try {
req = new XMLHttpRequest();
} catch(e) {
req = false;
}
} else if(window.ActiveXObject) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
req = false;
}
}
}
if(req) {
req.onreadystatechange = processReqChange;
req.open("GET", url + '?' + params, true);
req.send(null);
return true;
}
return false;
}
So far this is fairly generic and there are many similar examples on the web (see the links under References at the bottom of this page). The loadXMLDoc function accepts two parameters, with the first being the location of the server-side script to be called and the second being the variables to pass to that script:
Here's a quick example of how the script might be called:
<script src="xmlhttp.js"></script>
<script>
var params = 'q=' + encodeURIComponent(input) + '&target=' + encodeURIComponent(target);
loadXMLDoc('/scripts/myscript.php', params);
</script>
This will call the script located at /scripts/myscript.php with two GET parameters (q and target). It's good practice to encode the values using encodeURIComponent so that any special characters are properly encoded.
Using POST in place of GET
You'll notice that we've use the 'GET' rather than 'POST' method. This seems to be the most reliable at the moment for cross-platform applications, but for passing data to the server longer than 512 bytes you will need to use POST instead. The amount of data returned by the script is not an issue here - only what's sent.
If you want to POST data, you have to change the MIME type of the request using a Content-Type header, and pass the variables in the send call rather than as a part of the URL. The final part of the loadXMLDoc function then changes to:
if(req) {
req.onreadystatechange = processReqChange;
req.open("POST", url, true);
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.send(params);
return true;
}
The lines highlighted here simply replace those highlighted in the original code.
In most cases the third parameter of the open command can be left off. A value of true indicates that the call is to be 'asynchronous' - the script continues without waiting for an XML response - which is already the default behaviour. Passing a value of false causes the script to halt until the response is received.
Processing the response values
The script referred to by url is going to be a server-side script (PHP, Perl or similar) that returns a well-formed XML document. To avoid browser security warnings it must be hosted under the same domain as the calling page and be accessible via HTTP (or HTTPS).
The function that handles the response, in this case processReqChange, then needs to be able to unpack and process the XML that it receives. This diagram shows how data is passed from JavaScript to the server-side script and back:
The principle is fairly simple. The XML document returned will contain one or more 'commands' which are executed sequentially - to generate alerts, change form values, or manipulate the DOM. All of this is done by the processReqChange function.
There's no need to write new JavaScript functions unless you need something that's not in the current tool-set. All you need to worry about as a programmer are passing the url of the server-side script and any parameters to the loadXMLDoc function, and setting up the server-side script to return valid XML.
Available Commands
At this stage we've created a total of seven different commands that can be triggered by this library. More will be added over time as new applications are developed with new requirements. The seven are:
- alert (message)
- display a JavaScript alert
- setvalue (target, value)
- Set the value of a form field (with an id of target)
- setdefault (target)
- Reset the value of a form field
- focus (target)
- Set the focus to a form field
- setcontent (target, content)
- Set the innerHTML of an HTML element
- setstyle (target, property, value)
- Set the style of an HTML element
- setproperty (target, property, value)
- Set a property of an HTML element.
In each case target is an id referencing an element of the HTML page. Other values are either text or HTML. See the examples futher down the page.
The JavaScript code that does all this is as follows:
// Original JavaScript code by Chirp Internet: chirpinternet.eu
// Please acknowledge use of this code by including this header.
function getNodeValue(parent, tagName)
{
var node = parent.getElementsByTagName(tagName)[0];
return (node && node.firstChild) ? node.firstChild.nodeValue : false;
}
function processReqChange()
{
if(req.readyState == 4 && req.status == 200) {
var response = req.responseXML.documentElement;
var commands = response.getElementsByTagName('command');
for(var i=0; i < commands.length; i++) {
method = commands[i].getAttribute('method');
switch(method) {
case 'alert':
var message = getNodeValue(commands[i], 'message');
window.alert(message);
break;
case 'setvalue':
var target = getNodeValue(commands[i], 'target');
var value = getNodeValue(commands[i], 'value');
if(target && value) {
document.getElementById(target).value = value;
}
break;
case 'setdefault':
var target = getNodeValue(commands[i], 'target');
if(target) {
document.getElementById(target).value = document.getElementById(target).defaultValue;
}
break;
case 'focus':
var target = getNodeValue(commands[i], 'target');
if(target) {
document.getElementById(target).focus();
}
break;
case 'setcontent':
var target = getNodeValue(commands[i], 'target');
var content = getNodeValue(commands[i], 'content');
if(target && content) {
document.getElementById(target).innerHTML = content;
}
break;
case 'setstyle':
var target = getNodeValue(commands[i], 'target');
var property = getNodeValue(commands[i], 'property');
var value = getNodeValue(commands[i], 'value');
if(target && property && value) {
document.getElementById(target).style[property] = value;
}
break;
case 'setproperty':
var target = getNodeValue(commands[i], 'target');
var property = getNodeValue(commands[i], 'property');
var value = getNodeValue(commands[i], 'value');
if(value == "true") value = true;
if(value == "false") value = false;
if(target) {
document.getElementById(target)[property] = value;
}
break;
default:
window.console.log("Error: unrecognised method '" + method + "' in processReqChange()");
}
}
}
}
This code has now been deprecated and replaced by the AjaxRequest class which you can find presented in the next article.
You can see that it would be a trivial exercise to add more options to the processReqChange function. Our advice is to do so only when you absolutely have to and in a way that is as generic as possible.
We're very interested in any feedback/suggestions on how this code can be improved or finding out how it's being used in different applications. Contact us through the Feedback link at the bottom of the page.
Generating an XML response using PHP
The functions presented here are really aimed at PHP programmers who don't want to know too much about JavaScript. If that describes you then you'll be happy to know that once the initial request has been sent no further JavaScript is required.
Here's an XML file that generates an alert message. Clicking on the link below should generate a JavaScript alert box with the text "hello world!" - an important first step in any programming language:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
<command method="alert">
<message>hello world!</message>
</command>
</response>
The following XML response will load some text into an element on the page. In this case the output will appear in the div below which has an id of example2. Instead of a div we could just as easily be setting the contents of a heading, paragraph or table cell:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
<command method="setcontent">
<target>example2</target>
<content>hello world!</content>
</command>
</response>
As mentioned earlier you can insert HTML rather than text, and also return dynamic rather than static data. The following XML response is generated using PHP to insert the current date and time:
<?PHP
header('Content-Type: text/xml');
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . "\n";
?>
<response>
<command method="setcontent">
<target>example3</target>
<content><![CDATA[The time is:<br><b><?PHP echo gmdate('r'); ?></b>]]></content>
</command>
</response>
Note: Internet Explorer will cache the XML response and use it for future requests meaning that the output never changes. Look further down the page for a description of the solution. Other browsers normally make a new request each time.
Any HTML content needs to be enclosed using <![CDATA[ and ]]> to differentiate it from the XML.
And set styles - again using PHP to generate the XML response:
<?PHP
$colours = array('red', 'green', 'blue');
shuffle($colours);
header('Content-Type: text/xml');
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . "\n";
?>
<response>
<command method="setstyle">
<target>example4</target>
<property>backgroundColor</property>
<value>#ff0</value>
</command>
<command method="setstyle">
<target>example4</target>
<property>color</property>
<value><?PHP echo $colours[0] ?></value>
</command>
<command method="setstyle">
<target>example4</target>
<property>borderColor</property>
<value><?PHP echo $colours[1] ?></value>
</command>
</response>
Note: All DOM-enabled browsers seem to accept backgroundColor as the style name wheras background-color which is 'more proper' only works in some.
To show you just how useful this approach can be we've included two commands in this example and shown again how some or all of the values can be generated on-the-fly using PHP or another server-side language.
The first command sets the background-color property of the 'hello world' box to yellow (#ff0), and the second sets the text color to a random colour (red, green or blue).
Sat, 21 Dec 2024 12:31:54 +0000
The commands not covered above relate mostly to form fields - setting/resetting the value in a field and setting the focus. These are useful in real-time server-side form validation when you want to check data without reloading the current page.
And if you don't want to be writing all that XML code, we also have a PHP class for Generating an XML Response for Ajax Applications which does most of the work for you.
Making an XMLHttpRequest using JavaScript
There are a number of different examples you can view on this site of how to call the loadXMLDoc function. The following example takes a reference to a form as the first parameter and then has two additional variables, target and method. The params variable is then built using a value from the form plus the target and method parameters which are assumed to be already encoded, or not to need encoding:
function callAjax(form, target, method)
{
params = 'q=' + encodeURIComponent(form.input.value) + '&target=' + target + '&method=' + method;
return loadXMLDoc('/scripts/myscript.php', params);
}
The form itself might look like:
<form method="POST" action="nonAjaxTarget.html" onSubmit="return !callAjax(this, 'Target', 'Method');">
Input: <input type="text" name="input" value="">
<input type="submit" value="Click Me">
</form>
A successful call to loadXMLDoc() will return a value of true. The onSubmit handler will then return false, cancelling the default action of the submit event which would otherwise cause the form to be submitted. Further execution is handled by Ajax so the browser doesn't have to load a new page.
An unsuccessful call to loadXMLDoc will return a value of false. The onSubmit handler will then return true, causing the form to be submitted normally. Futher execution is handled by nonAjaxTarget.html.
This framework is being used by the following pages on The Art of Web:
- JavaScript: Escaping Special Characters
- JavaScript: Protecting mailto links
- HTML: ASCII Character Codes
Prevent caching of XMLHttpRequest response values
Some browsers will cache GET requests made using XMLHttpRequest so that after the first call any subsequent calls to the same script will simply re-load the first response. To get around that you need to append a random string or timestamp to the request as follows:
var params = 'q=' + encodeURIComponent(input) + '&target=' + encodeURIComponent(target);
params += '&' + (new Date()).getTime();
loadXMLDoc('/scripts/myscript.php', params);
If your script always returns the same response for given parameters then you don't need to (and indeed shouldn't) worry about this as you probably do want to take advantage of caching to speed up your application.
Steps to Implementation
To sum up how simple it is to work with Ajax using this framework, you only need to follow these steps:
- set up the server-side script to accept GET or POST (or COOKIE) parameters and return a valid XML file;
- include the JavaScript file xmlhttp.js somewhere on your page;
- use JavaScript to call the loadXMLDoc function with your parameters.
There are many more advanced (read complex) Ajax frameworks popping up on the web, but many seem to make sense only to the authors. Hopefully we've done a little better.
For assistance in generating a valid XML response using PHP, read the article Generating an XML Response for Ajax Applications where you'll find a PHP class that complements the preceding JavaScript functions.
References
- MDN: Using XMLHttpRequest
- MDN: Core JavaScript 1.5 Reference:Global Functions:encodeURIComponent
- MDN: AJAX:Getting Started
Related Articles - Ajax
- JavaScript Making an asynchronous request before following an href
- JavaScript Form Validation using Ajax
- JavaScript Using a Promise to make sequential Ajax requests
- JavaScript Avoiding the Race Condition with Ajax
- JavaScript Using XMLHttpRequest to log JavaScript errors
- JavaScript Making a HEAD request via an Ajax script
- JavaScript Recording Outbound Links using Ajax
- JavaScript Making sure form values are unique using Ajax
- PHP Generating an XML Response for Ajax Applications
- PHP Ajax script for making cURL HEAD requests
- JavaScript Web Services using XMLHttpRequest (Ajax)
Samanyu Mehra 2 November, 2020
Hello, This helped me a lot but my problem is I am getting the data from my servlet in json form but i am not able to access it in ajax/js. Your help would be appreciated. My JSON String is
[{"fName":"Samanyu","lName":"Mehra"},{"fName":"Rajesh","lName":"Khanna"}]
Shakti 15 May, 2014
Thanks. I was struggling to find a way to get rid of cookie issue generated by get call. Your explaination helped me over come that issue.. Thanks again for the post.
DEan 1 October, 2013
This may seem like an odd request but is there any way I can prevent it from sending anything back?
I just want to trigger a perl script that writes out a file. I don't wan't any data back
Yes, just return an empty XML file.
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<response>
</response>
ivan 6 August, 2013
so, very nice your tutorial. but, in my case, i need get a response of a webservice in other domain
End happens this error:
XMLHttpRequest cannot load
You know what can i do?
The XMLHttpRequest object is prevented from calling web services from outside its own domain - to prevent Javascript based cross-server attacks.
The workaround is to set up a local proxy that relays webservice calls using a local URL to the remote server.
Andy Low 17 February, 2006
This is a great website for me to pick up ajax! Thanks for putting it!
I would also like to find out how the "building menu ..." ajax works, the one similar to google "loading ...". It will be good if you can shed some light.
Thanks!!
Andy
Hi Andy, thanks for the positive feedback. The "building menu ..." feature isn't actually Ajax, but JavaScript (DHTML). You can find the source code in this article.
Chris Crawford 20 January, 2006
First off I got to say thanks for the tutorial and examples this works great. One thing I am trying to do is return a form text field and then set focus to it. I am no having any luck. If you can help me with this it would be greatly appreciated. Thanks,
Chris, try the first example on the Form Validation using Ajax page. If you enter an invalid email address and hit tab you should be returned to that field. If it still doesn't work send me your code or a link and I'll check it out.