This is a translation from my original post in spanish. I hope you enjoy it!
Note: Original post had code in HTML, sorry if I missed something.
A few days ago I was building a module that should make asynchronous requests to a webservice using javascript, in other words, the famous AJAX but manually made, without using any library. For those who do not know what AJAX is, it’s what allows server requests without having to load the entire page. Surely you’ve seen it in many places, for example, when posting a comment on a blog which is posted and shown without a full reload of the page.
So, in order to do this we need to use the XMLHttpRequest object of our browser using JavaScript and send a request to the server. The server will respond our request by sending us the data in an XML file, so we get it, read it, and display the result we want on screen using javascript.
Creating a XMLHttpRequest object
Let’s start, first of all… How do we create an XMLHttpRequest object? I’m going to show you the needed code which, since there’s no standard about that object yet (despite there’s a final schedule of the W3C dated 15 April of 2008) it is still necessary to place some bifurcations to create it dependidg on the browser.
function <strong>createXmlHttpRequestObject()</strong> { var req = false; if(window.XMLHttpRequest && !(window.ActiveXObject)) { 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;} } } return req; }
That object its what lets us send requests, we’ll save it on a pointer in each request this way:
var request; request = apicl_createXmlHttpRequestObject();
Making the request
Once we know how to create an XMLHttpRequest object, its time to make the request to the server, we have to have in account that each request to the server will create a new XMLHttpRequest object.
var request; //This variable has to be global. function callWS(url, responseHandler, parameters) { var strParameters = ""; request.onreadystatechange = responseHandler; request.open("POST", url, true); if(parameters != null && parameters.length != 0) { request.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); for(var i = 0; i < parameters.length; i++) { var p = parameters[i]; if(strParameters != "") strParameters += "&"; strParameters += p[0] + "=" + p[1]; } } request.send(strParameters); } //Request without parameters: function getDatos() { if (document.getElementById('divDatos').innerHTML==''){ request = createXmlHttpRequestObject(); callWS("http://www.entrecodigos.com/service.asmx/getDatos", getResponseDatos, null); } }
I’m going to try to explain the less clear points:
- The property onreadystatechange lets us establish which function will process the server response.
- The method open lets us assign certain values to our petition: the method (GET, POST or PUT), the url, if its an asynchronous request, user and password. The 3 last ones are optional.
- The request can be done with the parameters that the function you are calling may require, on that case we’ll put them in a chain. I’ll show you later how to use parameters.
- We make the request with the method send. To realize this request we are supposed to have a webservice or something in the other side that can handle this request. Notice that the url we are using its pointing a file, you can use a webservice or a simple aspx file that gets the request and just processes and answers it in XML, the first option its recommended.
Receiving the server answer
Now that we know how to make a request to the server, we have to receive the answer and process it, in my case I simplify this part making the server send me a simple text chain which I show in a div. Since Firefox gived me problems it placed long strings in different nodes I had to build a bucle:
function getResponse(){ var res = ''; for (var i=0; (i < request.responseXML.documentElement.childNodes.length); i++) { if (request.responseXML.documentElement.childNodes.item(i)) {res = res + request.responseXML.documentElement.childNodes.item(i).nodeValue;} } res = res.replace(/</g, "<").replace(/>/g, ">"); return res; } function getResponseDatos() { if(request.readyState == 4 && request.status == 200) { document.getElementById('divDatos').innerHTML=getResponse(); } }
As you can see, as the object changes its status (0: initialized, 1: request opened, 2: request sent, 3: receiving response, 4: response received) calls will be made to the function which we placed as the one to process the server response, thats why, we need to put a filter to know on which status we are when that function is called. When the status its finally 4, we check if the response has been satisfactory (200) or if there has been some problem (400, 404, …).
Requests with parameters
Finally, I’m going to show you the full code including an example of a request with parameters, which I think doesn’t need an explanation after all the wrote in this post.
//Peticiones Asíncronas: var request; //Esta variable ha de ser global. function <strong>createXmlHttpRequestObject()</strong> { var req = false; if(window.XMLHttpRequest && !(window.ActiveXObject)) { 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;} } } return req; } function callWS(url, responseHandler, parameters) { var strParameters = ""; request.onreadystatechange = responseHandler; request.open("POST", url, true); if(parameters != null && parameters.length != 0) { request.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); for(var i = 0; i < parameters.length; i++) { var p = parameters[i]; if(strParameters != "") strParameters += "&"; strParameters += p[0] + "=" + p[1]; } } request.send(strParameters); } function getResponse(){ var res = ''; for (var i=0; (i < request.responseXML.documentElement.childNodes.length); i++) { if (request.responseXML.documentElement.childNodes.item(i)) {res = res + request.responseXML.documentElement.childNodes.item(i).nodeValue;} } res = res.replace(/</g, "<").replace(/>/g, ">"); return res; } //Request without parameters: function getDatos() { if (document.getElementById('divDatos').innerHTML==''){ request = createXmlHttpRequestObject(); callWS("http://www.entrecodigos.com/service.asmx/getDatos", getResponseDatos, null); } } function getResponseDatos() { if(request.readyState == 4 && request.status == 200) { document.getElementById('divDatos').innerHTML=getResponse(); } } //Request with parameters: function getDatosConParam(){ var tipo = document.getElementById('divEjemplo'); if (tipo){ var parameters = new Array(); parameters[0] = new Array("nombreParam1", ejemplo.options[ejemplo.selectedIndex].value); parameters[1] = new Array("nombreParam2", "valor del param 2"); request = createXmlHttpRequestObject(); callWS("http://www.entrecodigos.com/service.asmx/getDatos", getResponseDatosConParam, parameters); } } function getResponseDatosConParam() { if(request.readyState == 4 && request.status == 200) { document.getElementById('divEjemplo').innerHTML=getResponse(); } }
Final notes
Just for preventing you from having the same problem I had, I warn you that javascript will only be able to send a request to the same server that has served you the page on which you are for security reasons (phishing and other stuffs), this is, if we try to call a webservice hosted in www.anotherweb.com from www.myweb.com we’ll get a permission error blocking the access. To do this you can, instead, use an iframe which loads from www.anotherpage.com on your page and then send the request, though, for the same security reasons, you will not be able to access the main frame objects from the iframe but you can still show some data in it.
Ive wrote manually that code Im showing without testing it, so it is possible it has some mistakes 😉