I'm trying to use an AJAX call to update a bunch of image elements on a page. The list of elements to update is held in an array and the URLs to assign to each image are retrieved from a PHP page via AJAX.
The code I have below doesn't work because imagesArray[i] is undefined when it is called from the callback function in the AJAX call - due to the asynchronous nature of JavaScript presumably.
var imagesArray = document.getElementsByClassName('swappableImages');
for (i = 0; i < imagesArray.length; i++) {
var requestUrl = "http://example.com/getAnImageURL.php";
getDataViaAJAX(requestUrl, function(data) {
alert('img url=' + data.responseText);
imagesArray[i].src = data.responseText;
});
}
function getDataViaAJAX(url, callback) {
var request = window.ActiveXObject ?
new ActiveXObject('Microsoft.XMLHTTP') :
new XMLHttpRequest;
request.onreadystatechange = function() {
if (request.readyState == 4) {
request.onreadystatechange = doNothing;
callback(request, request.status);
}
};
request.open('GET', url, true);
request.send(null);
}
function doNothing() {}
On reading around it seems that one way to solve this would be to use a closure, however closures are something I've still not managed to get my head around and the examples I have found have just confused me further.
So, how can I update each element in the array as an when the AJAX function returns?
Note that the 'duplicate' question that has been identified is a jQuery version of the question with jQuery specific answers. I am using vanilla JavaScript.
Note: First example/approach - referred to in comments - removed from answer.
You may try this:
var requestUrl = "http://example.com/getAnImageURL.php";
for (i = 0; i < imagesArray.length; i++) {
(function(j) {
getDataViaAJAX(requestUrl, function(data) {
alert('img url=' + data.responseText);
imagesArray[j].src = data.responseText;
});
})(i);
}
Related
I have scripted a simple Ajax function and embedded this to my website.
On the console I get this warning
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.
What does this mean and how to avoid it?
function readTextFile(file) {
var rawFile = new XMLHttpRequest();
var allText = "";
rawFile.open("GET", file, false);
rawFile.onreadystatechange = function () {
if (rawFile.readyState === 4) {
if (rawFile.status === 200 || rawFile.status == 0) {
allText = rawFile.responseText;
}
}
}
rawFile.send(null);
return allText;
}
function load() {
var allText = readTextFile('drinks.json');
var mydata = JSON.parse(allText);
var div = document.getElementById('cocktaillist');
div.innerHTML = "";
for (var i = 0; i < mydata.length; i++) {
div.innerHTML = div.innerHTML + "<p class='inner' id="+i+">"+ mydata[i].name +"</p>" + "<br>";
}
}
You need to change your code to use asynchronous Ajax requests. As the warning indicates, using synchronous requests is bad for the user experience. Also, there literally never is a reason to use synchronous Ajax requests, ever.
And that means you need to use callback functions. Compare:
function getText(path, success) {
var req = new XMLHttpRequest();
req.open("GET", path, true);
req.onreadystatechange = function () {
if (req.readyState === 4) {
if (req.status === 200) {
success(req.responseText); // call a function with the received value
} else {
console.log("unexpected response status " + req.status);
}
}
};
req.send(null);
}
function load() {
getText('drinks.json', function (allText) { // pass a function to call when the request is done
var mydata = JSON.parse(allText);
var div = document.getElementById('cocktaillist');
div.innerHTML = "";
for (var i = 0; i < mydata.length; i++) {
div.innerHTML = div.innerHTML + "<p class='inner' id="+i+">"+ mydata[i].name +"</p>" + "<br>";
}
});
}
The message you observed:
Synchronous XMLHttpRequest on the main thread is deprecated because of
its detrimental effects to the end user's experience.
Is a warning about the fact that a synchronous request defeats the asynchronous nature of JavaScript and blocks the user interface, which causes bad user experience, especially in case of bad network quality.
You should set true as third parameter of rawFile.open() and you already provided a rawFile.onreadystatechange callback in order to execute code when you have received a response, so the rest of your code is fine.
I was looking for AJAX in pure JavaScript (without jQuery) for learning purposes and came across this video, along with the code (shown below) on how to make one. However, it's geared towards GET method and I'm not sure how to tweak it to accept additional parameters so that the function can be used for either POST or GET depending on my specified parameters. For example, the lines xhr.open('GET', url, true); and xhr.send(''); both are GET-specific (all other lines in the function are the same for both GET and POST methods)--I want to be able to be able to specify whether to use POST or GET as a parameter for function load for xhr.open and a string such as "username="+username+"&password="+password for function load for xhr.send('');
For example, the function below is for GET and is used like this load('emails.php', function(xhr) {...}. I want the function to be used like this: load('emails.php', 'POST', '"username="+username+"&password="+password' function(xhr) {...}` for POST andload('emails.php', 'GET', '', function(xhr) {...}`
The function for AJAX for GET:
function load(url, callback) {
var xhr;
if(typeof XMLHttpRequest !== 'undefined') xhr = new XMLHttpRequest();
else {
var versions = ["Microsoft.XmlHttp",
"MSXML2.XmlHttp",
"MSXML2.XmlHttp.3.0",
"MSXML2.XmlHttp.4.0",
"MSXML2.XmlHttp.5.0"];
for(var i = 0, len = versions.length; i < len; i++) {
try {
xhr = new ActiveXObject(versions[i]);
break;
}
catch(e){}
} // end for
}
xhr.onreadystatechange = function() {
if((xhr.readyState < 4) || xhr.status !== 200) return;
callback(xhr);
};
xhr.open('GET', url, true);
xhr.send('');
}
Right now the request body is set to null and ignored since you are making a GET request. You should just be able to put whatever you want sent in the body of the POST request inside of the send method:
xhr.open('POST', url, true);
xhr.send(JSON.stringify(someJsonHere));
Check out the documentation for the xhr.send method here:
Here is also a link to a more thorough guide on using XMLHttpRequests; at the bottom it has a section specific to sending data.
I'm at peak-frustration trying to resolve my mental block re: callbacks. I've read How to return value from an asynchronous callback function? and How to return the response from an Ajax call? (among many other posts), and indeed the latter was helpful with another problem. However what I'm trying to do now is just slightly different and I'm losing my mind trying to adapt it to my code. Maybe my approach is entirely wrong/fundamentally flawed (and not just immature, which I can live with)?
The essence of my problem is that rather than simply returning ajax result to a callback function, I need the resulting json to be available to different functions, corresponding to different events, i.e.:
linkOne.onclick = invoke ajaxReq + getJsonData, then call functionOne with getJsonData result as an argument
linkTwo.onclick = invoke ajaxReq + getJsonData, then call functionTwo with getJsonData result as an argument
linkThree.onclick = invoke ajaxReq + getJsonData, then call functionThree with getJsonData result as an argument
Can't this be done with the link.onclick definition? Why doesn't this work:
linkThree.onclick = functionOne(getJsonData);
Here's my code:
function ajaxReq() {
var request = new XMLHttpRequest();
return request;
}
function getJsonData() {
var request = ajaxReq();
request.open("GET", "/myJSON.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["An Array in myJSON.json"];
// functionOne(myJsonArray); // callback: what if I need to pass this value to various functions?
return myJsonArray; // ... 'cause this ain't doin' it, and I don't know why
}
}
} // onreadystatechange
} // getJsonData
function functionOne(myJsonArray) {
var myJsonArray = getJsonData(); // why doesn't this work, since, in getJsonData, var request = ajaxReq(); returns an ajax request ?
}
And why, if var request = ajaxReq(); invokes ajaxReq function and returns its result to getJsonData, does var myJsonArray = getJsonData(); in functionOne not do the same?
Any help with this is much appreciated. (p.s. seeking a pure javascript fix, not jQuery.)
svs
As it has been answered in the links you have specified, that we cannot return value from asynchronous call to use it in a synchronous function call. So here is the trick -
Assign all the onclick listeners a common function.
link1.onclick = someCommonfunction;
link2.onclick = someCommonfunction;
link3.onclick = someCommonfunction;
And define the common function like following, which will have json data in the callback, and you can pass that data to any function call.
function someCommonfunction(e) {
/* this is the function which will be finally executed with json data after clicking */
var callback = function(jsonData) {
var myJsonArray = jsonData;
//do some condition check and call functionOne, functionTwo or functionThree
};
getJsonData(callback);
}
I modified getJsonData to call callback with the response data.
function getJsonData(callback) {
var request = ajaxReq();
request.open("GET", "/myJSON.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["An Array in myJSON.json"];
callback(myJsonArray);
}
}
} // onreadystatechange
} // getJsonData
I'm trying to make 2 (or more) ajax calls simultaneously. I don't want to use jQuery, only pure JavaScript.
Most of the time, it works. data1 will output data from sample.com/ajax1 and data2 will output data from sample.com/ajax2, but sometimes (1 from 10) the second AJAX call will display result from the first one.
Why is this happening? Both AJAX requests are requesting data from the same domain, but from different URLs. Is there any way how to prevent this behavior?
Here is the script:
// First AJAX
var xmlhttp1;
// Second AJAX
var xmlhttp2;
if (window.XMLHttpRequest) {
xmlhttp1 = new XMLHttpRequest();
} else {
xmlhttp1 = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp1.onreadystatechange = function() {
if (xmlhttp1.readyState == 4 && xmlhttp1.status == 200) {
data = JSON.parse(xmlhttp1.responseText);
console.log('data1: ' + data);
}
}
xmlhttp1.open("GET", "http://sample.com/ajax1", true);
xmlhttp1.send();
if (window.XMLHttpRequest) {
xmlhttp2 = new XMLHttpRequest();
} else {
xmlhttp2 = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp2.onreadystatechange = function() {
if (xmlhttp2.readyState == 4 && xmlhttp2.status == 200) {
data = JSON.parse(xmlhttp2.responseText);
console.log('data2: ' + data);
}
}
xmlhttp2.open("GET", "http://sample.com/ajax2", true);
xmlhttp2.send();
First of all, I recomment wrapping your xmlHttpRequest generation/handling in a function, so you don't duplicate code that much.
The problem you have there is that the data variable is global, so both ajax callbacks are using the same variable. You can fix it using the var keyword in both calls.
xmlhttp2.onreadystatechange = function() {
if (xmlhttp2.readyState == 4 && xmlhttp2.status == 200) {
var data = JSON.parse(xmlhttp2.responseText);
console.log('data2: ' + data);
}
}
Because you're not properly encapsulating data. The way you have it written, data is a global object, so it's available to be modified by either ajax call. Since ajax calls are asynchronous, this will lead to unpredictable values for data.
The problem is probably because you forgot to define data inside your function
anyway with this function you can create multiple requests and have more control over them..
var req={};
function ajax(a){
var i=Date.now()+((Math.random()*1000)>>0);
req[i]=new XMLHttpRequest;
req[i].i=i;
req[i].open('GET',a);
req[i].onload=LOG;
req[i].send();
}
function LOG(){
console.log(this.i,this.response);
delete req[this.i];//clear
}
window.onload=function(){
ajax('1.html');
ajax('2.html');
ajax('3.html');
}
uses xhr2... you need to modify the code to make it work with older browsers.
So I'm not too sure if I'm going to be able to explain this in a way that someone will be able to help me but here it goes:
When I call the function getFishing() I want it to get the "username" Element and put it in a var called get_name, then I want it to send that variable over to the XML_Fishing.php file where is is then used in a mysql query which is then parsed to XML data which is then re-read by the fishingUrl() function. The issue right now is that it's not passing the get_name variable to the XML_Fishing.php file. Can anyone see why from the code below? I didn't give the entire fishingUrl function because it is not relevant to the passing of the variable. It's just the rest of the function, after the data is returned from the XML data.
function getFishing(){
var get_name = escape(document.getElementById("username").innerHTML);
var name = "XML_Fishing.php?username=" + get_name;
fishingUrl(name, "XML_Fishing.php", function(data) {
............
............
............
}
function fishingUrl(name, url, callback) {
var request = window.ActiveXObject ?
new ActiveXObject('Microsoft.XMLHTTP') :
new XMLHttpRequest;
request.onreadystatechange = function() {
if (request.readyState == 4) {
request.onreadystatechange = doNothing;
callback(request, request.status);
}
};
request.open('GET', url, true);
request.send(null);
}
name is not being passed to the AJAX request - try changing
fishingUrl(name, "XML_Fishing.php", function(data)
to
fishingUrl(name, name , function(data)
that will make first parameter redundant - unless it was being used in the callback function.
This will set $_GET['username'] in the PHP script to the value of get_name from the Javascript