Ajax is a key tool in modern web application development. It allows you to send and receive data asynchronously to the server and then parse it in Javascript. Ajax is the abbreviation of Asynchronous JavaScript and XML (Asynchronous JavaScript and XML).
The name of the Ajax core specification is inherited from the Javascript object used to create and initiate requests: XMLHttpRequest. There are two levels of this specification. All mainstream browsers implement the first level, which represents the basic level of functionality. The second level extends the initial specification, incorporates additional events and some features to make it easier to collaborate with form elements, and supports some related specifications.
1. Ajax starts
The key to Ajax lies in the XMLHttpRequest object, and the way to understand this object is to look at an example. The following code shows the simple usage of the XMLHttpRequest object:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Example</title></head><body><div> <button>apples</button> <button>cherries</button> <button>bananas</button></div><div id="target"> Press a button</div><script type="application/javascript"> var buttons = document.getElementsByTagName("button"); for(var i=0; i<buttons.length; i++){ buttons[i].onclick = handleButtonPress; } //The script will call this function to respond to the button control's click event function handleButtonPress(e){ //Create a new XMLHttpRequest object var httpRequest = new XMLHttpRequest(); //Set an event handler for the onreadystatechange event httpRequest.onreadystatechange = handleResponse; //Use the open method to specify the HTTP method and the URL to request (that is, tell the httpRequest object what you want to do) httpRequest.open("GET", e.target.innerHTML+".html"); //No data is sent to the server here, so the send method has no parameters available httpRequest.send(); } //Processing the response//Once the script calls the send method, the browser will send a request to the server in the background. Because the request is processed in the background, Ajax relies on events to inform the progress of the request. function handleResponse(e){ //When the onreadystatechange event is triggered, the browser will pass an Event object to the specified handler function, and the target property will be set to the XMLHttpRequest associated with this event if(e.target.readyState == XMLHttpRequest.DONE && e.target.status == 200){ //Request successfully document.getElementById("target").innerHTML = e.target.responseText; //Show the content of the requested document} }</script></body></html>Three additional documentation is very simple:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Apples</title> <style> img { float:left;padding:2px;margin:5px;border: medium double black;background-color: lightgrey; width: 100px;height: 100px;} </style></head><body><p> <img src="../img/show-page/img_apples.jpg"/> Page for apples.</p></body></html>The effect is shown in the figure below:
As the user clicks on each fruit button, the browser executes asynchronously and retrieves the requested document, while the main document is not reloaded. This is typical Ajax behavior.
2. Using Ajax Events
After creating and exploring a simple example, you can start to dig into the features supported by the XMLHttpRequest object and how to use them in your request. The starting point is the additional events defined in the second level specification:
Most of these events are triggered at a specific point in time during the request. The two events are exceptions, readystatechange and progress, which can be triggered multiple times to provide progress updates.
When these events are scheduled, the browser uses a regular Event object for the readystatechange event and a ProgressEvent object for other events. The ProgressEvent object defines all members of the Event object and adds these members described in the following figure:
The following code shows how to use these events:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Example</title> <style> table {margin: 10px;border-collapse: collapse; float: left;} div{margin: 10px;} td,th{padding: 4px;} </style></head><body><div> <button>apples</button> <button>cherries</button> <button>bananas</button></div><table id="events"></table><div id="target"> Press a button</div><script type="application/javascript"> var buttons = document.getElementsByTagName("button"); for(var i=0; i<buttons.length; i++){ buttons[i].onclick = handleButtonPress; } var httpRequest; function handleButtonPress(e){ clearEventDetails(); httpRequest = new XMLHttpRequest(); httpRequest.onreadystatechange = handleResponse; httpRequest.onerror = handleError; httpRequest.onload = handleLoad;; httpRequest.onloadend = handleLoadEnd; httpRequest.onloadstart = handleLoadStart;; httpRequest.onprogress = handleProgress; httpRequest.open("GET", e.target.innerHTML+".html"); httpRequest.send(); } function handleResponse(e){ displayEventDetails("readystate("+httpRequest.readyState+")") if(e.target.readyState == XMLHttpRequest.DONE && e.target.status == 200){ document.getElementById("target").innerHTML = e.target.responseText; } } function handleError(e){ displayEventDetails("error",e);} function handleLoad(e){ displayEventDetails("load",e);} function handleLoadEnd(e){ displayEventDetails("loadend",e);} function handleLoadEnd(e){ displayEventDetails("loadend",e);} function handleLoadStart(e){ displayEventDetails("loadstart",e);} function handleProgress(e){ displayEventDetails("progress",e);} function clearEventDetails(){ document.getElementById("events").innerHTML = "<tr><th>Event</th><th>lengthComputable</th><th>loaded</th><th>total</th>"; } function displayEventDetails(eventName,e){ if(e){ document.getElementById("events").innerHTML +="<tr><td>"+eventName+"</td><td>"+ e.lengthComputable+"</td><td>"+ e.loaded+"</td><td>"+ e.total+"</td></tr>"; }else { document.getElementById("events").innerHTML += "<tr><td>"+eventName+"</td><td>NA</td><td>NA</td><td>NA</td><td>NA</td></tr>"; } }</script></body></html>This is a variation of the previous example, registering a handler for some events and creating a record for each event processed in a table element. From the following image, you can see how the Firefox browser triggers these events.
3. Handle errors
Two types of errors must be paid attention to when using Ajax. The difference between them stems from different perspectives.
The first type of error is a problem seen from the perspective of the XMLHttpRequest object: Some factors prevent the request from being sent to the server. For example, DNS cannot resolve hostname, connection request is denied, or URL is invalid.
The second type of problem is the problem seen from the application perspective, not the XMLHttpRequest object. They occur when the request is successfully sent to the server, which receives the request, processes and generates a response, but the response does not point to what you expect. For example, if the requested URL does not exist, this kind of problem occurs.
There are three ways to deal with these errors, as shown in the following code:
3.1 Handling Setting Errors
The first type of problem that needs to be dealt with is passing incorrect data to the XMLHttpResquest object, such as an incorrect URL. They are extremely prone to occur when generating URLs based on user input. To simulate this kind of problem, the above document has a button that adds a tag Bad URL (wrong URL). Pressing this button will call the open method in the following form:
httpRequest.open("GET","http://")
This is an error that prevents the request from being executed, and an error is thrown when an event like this occurs in the XMLHttpRequest object. This means that a try...catch statement is needed to surround the code that sets the request, like this:
try{ ... httpRequest.open("GET","http://") ... httpRequest.send(); }catch(error){ displayErrorMsg("try/catch",error.message) }The catch clause gives you a chance to recover from the error. You can choose to prompt the user for a value, or fall back to the default URL, or simply discard the request. In this example, the displayErrorMsg function is called to display the error message.
3.2 Processing request errors
The second type of error occurs when the request has been generated but there are other errors. To simulate this type of problem, a button labeled Bad Host (Error Host) was added in the example. When this button is pressed, the open method will be called to access an unavailable URL:
httpRequest.open("GET",http://www.ycdoitt.com/nopage.html)There are two problems with this URL. The first problem is that the hostname cannot be resolved by DNS, so the browser cannot generate a server connection. This problem knows that the XMLHttpRequest object only becomes obvious when it starts generating the request, so it signals errors in two ways. If you register a listener for an error event, the browser will send an Event object to your listener. Here are the functions used in the example:
function handleError(e){ displayErrorMsg("Error event",httpRequest.status + httpRequest.statusText); }When such errors occur, the degree of information that can be obtained from the XMLHttpRequest object depends on the browser. Unfortunately, in most cases, the status with a value of 0 and a blank statusText value will be obtained.
The second problem is that the URL and the generated request have different sources, which is not allowed by default. You can usually only send Ajax requests to the same-origin URL that loads the script. When a browser reports this problem, an error may be thrown or an error event may be triggered. Different browsers handle it differently. Different browsers also check the source at different points in time, which means that the browser may not always be seen highlighting the same problem. Cross-Origin Resource Sharing can be used to bypass homologous restrictions.
3.3 Handling application errors
The last type of error occurs when the request completes successfully (from the perspective of the XMLHttpRequest object), but does not return the data you want. To create such problems, add a button with the label cucumber in the example above. Pressing this button will generate a request URL similar to the apples, cherries, and bananas buttons, but the cucumber.html document does not exist on the server.
There is no error in this process itself (because the request has been completed), and it is necessary to determine what is happening based on the status attribute. When requesting an existing document, the status code 404 will be obtained, which means that the server cannot find the requested document. You can see how the example handles status codes other than 200 (meaning OK):
if(httpRequest.status == 200){ target.innerHTML = httpRequest.responseText; }else{ document.getElementById("statusmsg").innerHTML = "Status:" + httpRequest.status +" "+ httpRequest.statusText; }In this example, the values of status and statusText are simply displayed. In real applications, recovery needs to be carried out in a useful and meaningful way (such as displaying alternate content or warning users that there is a problem, depending on which one is more suitable for the application).
4. Get and set headers
Using the XMLHttpRequest object, you can set the request header sent to the server and the header in the server response.
4.1 Overwrite the HTTP method for requests
It is usually not necessary to add or modify the header in the Ajax request. The browser knows what to send, and the server knows how to respond. However, there are several exceptions. The first one is the X-HTTP-Method-Override header.
The HTTP standard is often used to request and transmit HTML documents on the Internet, and it defines many methods. Most people know about GET and POST because they are the most widely used. However, there are other methods (including PUT and DELETE) that are used to give meaning to the URL requested to the server, and this usage is on the rise. For example, if you want to view a user record, you can generate such a request:
httpRequest.open("GET","http://myserver/records/freeman/adam");Only the HTTP method and requested URL are displayed here. For this request to work smoothly, the server side must be able to understand the request by the application and convert it into a suitable piece of data to be sent back to the server. If you want to delete the data, you can write it like this:
httpRequest.open(" DELETE ","http://myserver/records/freeman/adam");The key here is to express what you want the server to do via HTTP, rather than encode it into the URL in some way.
The problem with using HTTP methods in this way is that many mainstream web technologies only support GET and POST, and many firewalls only allow GET and POST requests to pass. There is an idiomatic approach to avoid this limitation, which is to use the X-HTTP-Method-Override header to specify the HTTP method you want to use, but the form is marketed and sending a POST request. The code demonstration is as follows:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Example</title></head><body><div> <button>apples</button> <button>cherries</button> <button>bananas</button></div><div id="target">Press a button</div><script> var buttons = document.getElementsByTagName("button"); for(var i = 0; i < buttons.length; i++){ buttons[i].onclick = handleButtonPress; } var httpRequest; function handleButtonPress(e){ httpRequest = new XMLHttpRequest(); httpRequest.onreadystatechange = handleResponse; httpRequest.open("GET", e.target.innerHTML+".html"); httpRequest.setRequestHeader("X-HTTP-Method-Override","DELETE"); httpRequest.send(); } function handleError(e){ displayErrorMsg("Error event",httpRequest.status+httpRequest.statusText); } function handleResponse(){ if(httpRequest.readyState == 4 && httpRequest.status == 200){ document.getElementById("target").innerHTML = httpRequest.responseText; } }</script></body></html>In this example, the setRequestHeader method on the XMLHttpRequest object is used to indicate that the request is wanted to be processed in the form of an HTTP DELETE method. Please note that I set this header only after calling the open method. If you try to use the setRequestHeader method before the open method, the XMLHttpRequest object throws an error.
PS: Overwriting HTTP requires the server-side web application framework to understand the convention of X-HTTP-Method-Override, and your server-side application must be set to find and understand those less HTTP methods.
4.2 Disable content caching
The second useful header that can be added to Ajax requests is Cache-Control, which is especially useful when writing and debugging scripts. Some browsers cache content obtained through Ajax requests and will not request it again during the browsing session. For the previous example, it means that changes on apples.html, cherries.html and bananas.html will not be immediately reflected in the browser. The following code shows how to set headers to avoid this:
httpRequest = new XMLHttpRequest(); httpRequest.onreadystatechange = handleResponse; httpRequest.open("GET", e.target.innerHTML+".html"); httpRequest.setRequestHeader("Cache-Control","no-cache"); httpRequest.send();The way to set the header is the same as in the previous example, but this time the header is Cache-Control, and the desired value is no-cache. After placing this statement, if the content requested through Ajax changes, it will be reflected the next time the document is requested.
4.3 Read the response header
The HTTP header sent by the server when responding to an Ajax request can be read through the getResponseHeader and getAllResponseHeaders methods. In most cases, you don't need to care about what's in the header, because they are part of the interactive transactions between the browser and the server. The following code shows how to use this property:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta content="width=device-width,user-scalable=no" name="viewport" /> <meta name="author" content="ye ChaoLuka" /> <meta name="description" content="A simple example" /> <title>Example</title> <link href="../img/ycdoit.ico" type="image/x-icon" rel="shortcut icon" /> <style> #allheaders,#cheader{border: medium solid black;padding: 2px;margin: 2px;} </style></head><body><div> <button>apples</button> <button>cherries</button> <button>bananas</button></div><div id="cheader"></div><div id="allheaders"></div><div id="target">Press a button</div><script> var buttons = document.getElementsByTagName("button"); for(var i = 0; i < buttons.length; i++){ buttons[i].onclick = handleButtonPress; } var httpRequest; function handleButtonPress(e){ httpRequest = new XMLHttpRequest(); httpRequest.onreadystatechange = handleResponse; httpRequest.open("GET", e.target.innerHTML+".html"); httpRequest.setRequestHeader("Cache-Control","no-cache"); httpRequest.send(); } function handleResponse(){ if(httpRequest.readyState==2){ document.getElementById("allheaders").innerHTML = httpRequest.getAllResponseHeaders(); document.getElementById("cheader").innerHTML = httpRequest.getResponseHeader("Content-Type"); }else if(httpRequest.readyState == 4 && httpRequest.status == 200){ document.getElementById("target").innerHTML = httpRequest.responseText; } }</script></body></html>The renderings are as follows:
According to this figure, we can see that the web server software that the development server is running is IntelliJ IDEA 15.0.4, and the last time the apples.html document was modified was June 27 (but the screenshot was July 5).