I am learning to use xmlhttprequest/AJAX. In this sample code from w3schools, I do not understand why this line:
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
precedes this:
xmlhttp.open("GET","demo_get.asp",true);
xmlhttp.send();
The way I'm thinking about it, you should send the GET request before you have any responseText to do anything with. Where is the error in my understanding?
<html>
<head>
<script type="text/javascript">
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","demo_get.asp",true);
xmlhttp.send();
}
</script>
</head>
<body>
<h2>AJAX</h2>
<button type="button" onclick="loadXMLDoc()">Request data</button>
<div id="myDiv"></div>
</body>
</html>
The line in question is inside of xmlhttp.onreadystatechange, which is a function. Note how it is used:
xmlhttp.onreadystatechange = function ()
{
...
}
In this case, it is a callback function - it is called when the ajax request (aka xmlhttp.send()) completes.
You might want to brush up on your javascript before you dive into ajax.
You have just discovered the asynchronous part of the AJAX word :-)
Even though the .send() method is called later, the innerHTML call is made earlier.
How come that works?!
Because an AJAX call is asynchronous. Thus, it's not like making a database call in PHP: you make your call, waits for the result, and work with it. Nope.
In JS, for AJAX calls, you define a callback function. It is a function that will be called once the response has arrived.
For the XMLHttpRequest object, it is the onreadystatechange event that is fired when the response comes back. If you register a function in this event, this function will be called when the response will come back.
P.S.: the function in onreadystatechange won't exactly be fired once the response comes back, but this was for the sake of the explanation. To know when this event is fired, take a look at the different states.
It doesn't. Consider this code:
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
It doesn't execute the code inside the function, it only creates the function and assigns it to a property. The function will be executes by the XMLHTTP object when the state changes, and it will catch the state change that means that the response has arrived.
Related
Is it possible to have multiple ajax calls on the same page, at the same time to different receiving div tags? I am struggling with finding answer to this.
I have 3 pages. A home page and 2 php pages: status and alert pages echoing the results.
Inside the home page I have 2 divs that get their data changed using ajax.
<div id="statusTable"> </div>
<div id="alertsTable"> </div>
Using setInterval I make 2 requests for new data for the divs at the same time. My problem is that both divs have the same data in them once the call is made - it's as if only one call was made for both.
setInterval
(
function()
{
getAlerts();
getStatus();
},
1000
);
I get this eg.
alerts table // getStatus() call
2 2
2 2
status table // getStatus()
2 2
2 2
instead of
alerts table //getAlerts()
1 1
1 1
status table //getStatus()
2 2
2 2
This is the code:
function loadXMLDoc(url,cfunc)
{
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
function getAlerts()
{
loadXMLDoc("alerts.php?update=variable",function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("alertsTable").innerHTML=xmlhttp.responseText;
}
});
}
function getStatus()
{
loadXMLDoc("status.php?update=variable",function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("statusTable").innerHTML=xmlhttp.responseText;
}
});
}
I changed the timer setIntervals so the calls don't overlap the data received in the divs like below
setInterval
(
function()
{
getAlerts();
},
5000
);
setInterval
(
function()
{
getStatus();
},
3000
);
I am hoping there is a way to make a call at the same time and not have to worry about my divs having the same content.
First, AJAX uses HTTP protocol which is stateless. If you can't be sure which answer reached first unless you tag them yourself in the response body.
Maybe the 2 reaches first and set, as receiving only one packet results in readyState change and drops other.
Your xmlhttp variable is defined globally. So by that, the packet with 1 inside is overloaded by packet with 2 inside. Try using var xmlhttp while defining it in your getAlert and getStatus functions and provide it to your loadXMLDoc function as a parameter.
Second, make sure whether the results should be different or not. Maybe it is just a coincident that both values are the same.
You need to decalre the xmlhttp variable locally in loadXMLDoc()
function loadXMLDoc(url,cfunc)
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
Without doing this, the xmlhttp value that is improperly declared in the function "leaks" into the global scope, meaning that you end up overwriting the values with whichever is called second (including the url value set on the object). You in essence introduce a race condition into your code due to asynchronous nature of the calls.
The race condition and the xmlhttp being global are the problems here.
What ever came last is overwriting the data written in the divs. Solve it by making xmlhttp variable
local as per suggestions and tagging the response body.
function loadXMLDoc(xmlhttp,url,cfunc)
{
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
function getAlerts()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
loadXMLDoc(xmlhttp,"alerts.php?update=variable",function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200 && xmlhttp.responseText.indexOf("unique key") != -1)
{
//change div which matches unique key
}
});
}
Credit: #Mike Brant and #Cunning
Before people like me want to continue and use more ajax calls check out
how browsers handle and process multiple ajax calls:
How many concurrent AJAX (XmlHttpRequest) requests are allowed in popular browsers?
http://sgdev-blog.blogspot.com/2014/01/maximum-concurrent-connection-to-same.html
What I am trying to do is to make two ajax calls and make each result show up in two different divs of the same page.
Here is my Javascript code:
function firstLoad(){
firstDiv(datedisplay.value);//populating first div
secondDiv(datedisplay.value);//populating second div
}
function loadXMLDoc(url,cfunc){
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
function firstDiv(changed_date){
var divided_date = changed_date.split("-");
loadXMLDoc("getmonth.php?m="+divided_date[0]+"&y="+divided_date[1],function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("timetableview").innerHTML=xmlhttp.responseText;
}
});
}
function secondDiv(changed_date){
var divided_date = changed_date.split("-");
loadXMLDoc("getmonthexp.php?m="+divided_date[0]+"&y="+divided_date[1],function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("expensesview").innerHTML=xmlhttp.responseText;
}
});
}
The response content is correct, however what happens now is that:
The second div is filled correctly.
The first div stays in blank, and if I keep refreshing the page, eventually the content of the second div will show up in the first div as well(weird).
Hope you guys can help me.
xmlhttp is global because you don't use the var keyword. This means that the onreadystatechange function you use for one will overwrite the other.
function loadXMLDoc(url, cfunc) {
var xmlhttp;
xmlhttp is global because you don't use the var keyword. This means that the onreadystatechange function you use for one will overwrite the other.
function loadXMLDoc(url, cfunc) {
var xmlhttp;
I am running an ajax request to retrieve a value of either 0,1, or 2 based upon some mysql code in the "check_answer_status.php" file. For test purposes, I have included the alert to check whether the general ajax and mysql request works fine and it does, hence the value contained within "Questiions.answerStatus" at the time of the alert is correct. However, my problem is that the function "checkAnswerStatus" has already executed and did not change the inital value of "answerStatus" (which I set to 50 for test purposes).
Context: sometime later in the code I want to execute code dependent on the value of the variable "answerStatus".
I believe I need to somehow include something like an "oncomplete" or something comparable, but I do not know how to do that. Can anyone help me out? Many thanks!
var = Questions = {
answerStatus:50,
checkAnswerStatus : function(question){
var xmlhttp;
if (window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
test = xmlhttp.responseText;
Questions.answerStatus = test;
alert(Questions.answerStatus);
}
}
xmlhttp.open("POST","../../include/check_answer_status.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("q="+question);
},
The request you make is asynchronus (the third parameter of the xmlhttp.open function). If you changed it to:
xmlhttp.open("POST","../../include/check_answer_status.php",false);
it should work.
Another options is to pass a callback to your checkAnswerStatus function, and call the callback when the request finishes. Example:
checkAnswerStatus : function(question, callback){
var xmlhttp;
if (window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
test = xmlhttp.responseText;
Questions.answerStatus = test;
callback(Questions.answerStatus); //call the function
}
}
xmlhttp.open("POST","../../include/check_answer_status.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("q="+question);
}
and then you will call the function like this:
Questions.checkAnswerStatus("bla bla", function(answerStatus) {
alert(answerStatus);
});
in addition to Nemos answer I would recommend you read following resources from MDN:
More technical API documentation for a brief overview of all the possibilities:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
More real life usecases:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
Hope this helps
On w3schools.com(url) there is an example of how to do an AJAX call with plain Javascript. If you look at the example you will see the call is triggered by a button:
<button type="button" onclick="loadXMLDoc()">Change Content</button>
This is the function:
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","ajax_info.txt",true);
xmlhttp.send();
}
What I would like to do is get the URL of the outgoing AJAX call which is ajax_info.txt(url):
xmlhttp.open("GET","ajax_info.txt",true);
Im trying to put that URL in to an alert, so I tried calling the headers of the response using getAllResponseHeaders() hoping that it will give me the Host like so:
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
alert(xmlhttp.getAllResponseHeaders());
It does give me all the headers but not the Host. So my next move was trying to set the Host myself using setRequestHeader() but then I realized the Header needs a Value which I had to send myself, so this will not work. What else can I try to get/fetch the outgoing AJAX URL in the alert?
Please note the code is just an example and I know that changing headers(in this case) is prohibited because of Access-Control-Allow-Origin.
I'm not sure how much access you have to the code but you can over-ride XMLHttpRequest.open and hook the url there.
XMLHttpRequest.prototype.open = (function(open) {
return function(method,url,async) {
console.log('the outgoing url is ',url);
open.apply(this,arguments);
};
})(XMLHttpRequest.prototype.open);
Here is a FIDDLE.
i am using a google map api to get location. i have used the below function onchange of address field. it is working properly onchange. i have used same function on submit in the same form to checking the function once again while submitting using a onsubmit attribute. but while submitting i cant able to get answer from this ajax because of the that return by using this return true the form submit with pout getting answer.
please any one find a solution for this
function validatePlaceForm(){
var frm = document.frmOncampus;
var oncampusAddress = frm.oncampusAddress.value;
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
//document.getElementById("my_div").innerHTML=''
var result =xmlhttp.responseText;
//alert(result);
var both =result.split("|");
frm.oncampusLatitude.value=both[0];
frm.oncampusLongtitude.value=both[1];
}
}
var url='latlog.php?address='+oncampusAddress;
xmlhttp.open("GET",url,true);
xmlhttp.send();
return true;
}
Unfortunately this will never work because the way you're using the XHR there is asynchronous:
xmlhttp.open("GET",url,true); // true means asynchronous
As the call is asynchronous the result of the function will have no result on the running of the Ajax request.
If you want to continue with using this function just change this last parameter to false, i.e.
xmlhttp.open("GET", url, false); // will now be synchronous
You also won't need a onreadystatechange handler now. Bear in mind that this will obviously make the XHR call synchronous on your onchange event too; maybe customise the function to accept a flag for is_synchronous or something.
You might also want to consider adding a timestamp onto your requests so that the browser does not cache the response.
Change the return value from true to false and call this function like this:
onsubmit="return validatePlaceForm();"
This should do the trick.
Check for the response coming from AJAX. If response is blank then return false, otherwise return true.