XHR request in for loop fires only once - javascript

Im making a website for this spa bussiness. It has four horizontal sections with several buttons each, every button should display its own "treatment information" in the div "#container_xhr_info".
To avoid stuffing the HTML I put all the treatment divs in their own html file in a folder, so when making click on a button the XHR request should fetch the button's file and display it inside the container.
Ive added a Click Event to the buttons with a for loop, the event fires the xhr. I also saved the file URLs in an array to put them inside the open() also with a for loop. So far the code works but it displays the 4th URL in the array on every button (the "peeling" one). I havent found a solution to this specific case.
let array_asyncs_facial_location = ["Asyncs/facial/radio.html",
'Asyncs/facial/diamante.html',
'Asyncs/facial/limpieza.html',
'Asyncs/facial/peeling.html',
'Asyncs/facial/acne.html',
'Asyncs/facial/rosacea.html',
'Asyncs/facial/pustulas.html'];
let tratamientos_facial = document.getElementsByClassName('tratamientos_facial');
for(var i = 0; i < tratamientos_facial.length; i++){
tratamientos_facial[i].addEventListener('click', ()=>{
let xhr = new XMLHttpRequest;
let url = array_asyncs_facial_location[i];
xhr.open('GET', url );
xhr.addEventListener('load', ()=>{
if (xhr.status == 200){
let plantilla = xhr.response;
let container_xhr_info = document.querySelector('#container_xhr_info');
container_xhr_info.innerHTML = plantilla;
}
})
xhr.send();
})
}

The variable i that you're relying on to get your URL isn't resolved until the click function fires, at which point it's not the same value as when you set up the listener. There's a better way. Pass through the event argument in your listener and set the url dynamically using element.setAttribute(), then get the value in element.dataset. Setting an attribute like data-url allows us to get it simply with element.dataset.url
let array_asyncs_facial_location = ["Asyncs/facial/radio.html",
'Asyncs/facial/diamante.html',
'Asyncs/facial/limpieza.html',
'Asyncs/facial/peeling.html',
'Asyncs/facial/acne.html',
'Asyncs/facial/rosacea.html',
'Asyncs/facial/pustulas.html'
];
let tratamientos_facial = document.getElementsByClassName('tratamientos_facial');
for (var i = 0; i < tratamientos_facial.length; i++) {
// set the URL as an attribute of the element itself so we can get it later
tratamientos_facial[i].setAttribute('data-url', array_asyncs_facial_location[i]);
tratamientos_facial[i].addEventListener('click', (event) => {
// make sure to pass the event argument through
let xhr = new XMLHttpRequest;
// now we just query the dataset property
let url = event.target.dataset('url');
xhr.open('GET', url);
xhr.addEventListener('load', () => {
if (xhr.status == 200) {
let plantilla = xhr.response;
let container_xhr_info = document.querySelector('#container_xhr_info');
container_xhr_info.innerHTML = plantilla;
}
})
xhr.send();
})
}

Related

API response is giving two responses when only one is requested

I'm making an API request in JS that is working, but it keeps giving two responses when I'm only expecting one. Here's the code:
CallCoinGecko();
function CallCoinGecko() {
var cgrequest = new XMLHttpRequest();
var url1 = 'https://api.coingecko.com/api/v3/simple/price?ids=';
var id = 'ethereum';
var url2 = '&vs_currencies=USD&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true';
cgrequest.open("GET", url1 + id + url2, true);
cgrequest.send();
cgrequest.onreadystatechange = function () {
if (cgrequest.status == 200) {
var cgresponse = cgrequest.responseText;
var cgobj = JSON.parse(cgresponse);
console.log(cgobj);
var cgprice = (cgobj[id]['usd']);
console.log(cgprice);
}}}
Here is a console image that shows two responses:
image
Any thoughts on this are appreciated thanks
Try verifying the value of readyState after the request. Example:
if (cgrequest.status === 200 && cgrequest.readyState === 4)
The problem is that, as mentioned, the readyState can have multiple values - therefore everytime it changes, your code to print the response text will trigger. I'm sure if you looked in the network tab, the API request isn't being carried out two times, only your response text is being printed two times. Checking if it is 4 confirms that the request is done - and should only be carried out once.
My hunch that the API isn't being called two times is due to the fact that the data is exactly the same on each console.log().
Good luck :)
You should compare the readyState value in your if statement as well.
function CallCoinGecko() {
var cgrequest = new XMLHttpRequest();
var url1 = 'https://api.coingecko.com/api/v3/simple/price?ids=';
var id = 'ethereum';
var url2 = '&vs_currencies=USD&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true';
cgrequest.open("GET", url1 + id + url2, true);
cgrequest.onreadystatechange = function(res){
if (cgrequest.status === 200 && cgrequest.readyState === XMLHttpRequest.DONE) {
var cgresponse = cgrequest.responseText;
var cgobj = JSON.parse(cgresponse);
console.log(cgrequest.status, cgobj);
var cgprice = (cgobj[id]['usd']);
console.log(cgrequest.status, cgprice);
}
};
cgrequest.send();
}

Can't request from website multiple times

I'm trying to make a pokedex for a school project, and am using JS for doing so. I found this api called pokeapi, and since it uses JSON, I thought of just getting the data in the page and returning a json of it, however when I try to use the method I created to do a request in a for loop, it doesn't seem to work, only working on the last element of the for loop:
let request = new XMLHttpRequest();
const getJSON = (link, action) => {
request.open("GET", link);
request.send();
request.onload = () => {
if (request.status === 200) {
let json = JSON.parse(request.response);
action(json);
} else {
console.log(`e:${request.status} ${request.statusText}`);
}
}
}
let counter = 1;
getJSON("https://pokeapi.co/api/v2/pokedex/1/", (json) => {
json.pokemon_entries.forEach((poke_entry) => {
getJSON(poke_entry.pokemon_species.url, (poke_sp) => {
console.log(poke_sp);
//console loggin poke_sp only shows one console log, the last member of `json.pokemon_entries`
})
});
});
This is because you have created a single XHR object.
Every time you make a request with it, you cancel the previous request.
Create a new XHR object (inside the getJSON function) for each request.
i.e. swap the order of
let request = new XMLHttpRequest();
and
const getJSON = (link, action) => {

What is different about xmlhttprequest in Firefox

My code works in Chrome and Safari, but it hangs in FF.
I removed the parts of the code that aren't necessary.
I used console commands to show how far the first loop gets, and it will do the second log fine right before the xhr open and send commands.
If the open/send commands are present the loop only happens once, if I remove the open/send commands the loop completes successfully.
Currently using FF 62nightly, but this issue has plagued me since Quantum has come out and I'm now trying to figure out why it doesn't work right.
for (i = 0; i < length; i++) {
(function(i) {
// new XMLHttpRequest
xhr[i] = new XMLHttpRequest();
// gets machine url from href tag
url = rows[i].getElementsByTagName("td")[0].getElementsByTagName('a')[0].getAttribute('href');
// Insert the desired values at the end of each row;
// will try to make this customizable later as well
insertVNC[i] = rows[i].insertCell(-1);
insertSerial[i] = rows[i].insertCell(-1);
insertVersion[i] = rows[i].insertCell(-1);
insertFreeDiskSpace[i] = rows[i].insertCell(-1);
// the fun part: this function takes each url, loads it in the background,
// retrieves the values needed, and then discards the page once the function is complete;
// In theory you could add whatever you want without taking significantly longer
// as long as it's on this page
console.log(i);
xhr[i].onreadystatechange = function() {
if (xhr[i].readyState == 4 && xhr[i].status == 200) {
}
};
//"Get" the "Url"... true means asyncrhonous
console.log(url);
xhr[i].open("GET", url, true);
xhr[i].send(null);
})(i); //end for loop
}
I cannot tell you why it gives issues in Firefox. I would not trust sending off arbitrarily many requests from any browser
I would personally try this instead since it will not fire off the next one until one is finished
const urls = [...document.querySelectorAll("tr>td:nth-child(0) a")].map(x => x.href);
let cnt=0;
function getUrl() {
console.log(urls[cnt]);
xhr[i].open("GET", urls[cnt], true);
xhr[i].send(null);
}
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr[i].readyState == 4 && xhr[i].status == 200) {
if (cnt>urls.length) getUrl();
cnt++;
}
}
getUrl();

How to delay a method until another is finished first , javascript?

I'm currently working on a project for school using a pokemon api that will display the information needed to evolve the pokemon (please note that I'm completely new to javascript and HTML).
Link :http://pokeapi.co/docsv2/
The website will ask the user for a name and that name will be used to get a url for the main information that I'm looking for.
For example : if someone enters in pikachu, the program will request the object for pikachu which contains the url for pikachu's evolution chain and that url is the one that will provide the main information for the website.
Currently the code looks like this:
var pokemon = new XMLHttpRequest();
var name = prompt("Whats the name of the pokemon you have?").toLowerCase();
var url = "http://pokeapi.co/api/v2/pokemon-species/" + name;
var url2;
pokemon.onreadystatechange = function(){
if(pokemon.readyState == 4 && pokemon.status == 200){
var myArr = JSON.parse(pokemon.responseText);
var url2 = myArr.evolution_chain;
}
}
pokemon2.onreadystatechange = function() {
if (pokemon2.readyState == 4 && pokemon2.status == 200) {
var myArr2 = JSON.parse(pokemon2.responseText);
console.log(myArr2.chain.species.name);
}
}
var pokemon2 = new XMLHttpRequest();
pokemon2.open("GET", url2, true).done(onreadystatechange);
pokemon2.send();
pokemon.open("GET", url, true);
pokemon.send();
However the program doesn't work due to the fact that the getting is occurring at the same time and pokemon2 should only be called after pokemon is finished because it's getting the actual url for pokemon2.
Does anyone know how to be able to accomplish this?
Many thanks! :).
You can call pokemon2 once pokemon finishes:
pokemon.onreadystatechange = function(){
if(pokemon.readyState == 4 && pokemon.status == 200){
var myArr = JSON.parse(pokemon.responseText);
var url2 = myArr.evolution_chain;
// Call pokemon2 here
var pokemon2 = new XMLHttpRequest();
pokemon2.open("GET", url2, true);
pokemon2.send();
}
}

making parallel ajax requests

i have javascript code that does these things in a loop
create a div element,append it to the dom and get its reference
pass this reference to a function that makes an ajax post request
set the response of the ajax request to the innerHTML of the passed element reference
here is the code
window.onload = function () {
var categories = document.getElementById('categories').children;
for (i = 0; i < categories.length; i++) {
var link = categories[i].children[1].children[0].attributes['href'].nodeValue;
var div = document.createElement('div');
div.className = "books";
div.style.display = "none";
categories[i].appendChild(div);
getLinks(link, div);
}
}
function getLinks(url, div) {
xhr = new XMLHttpRequest();
xhr.open('POST', 'ebook_catg.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
url = encodeURIComponent(url)
var post = "url=" + url;
xhr.node=div; //in response to Marc B's suggestion
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
xhr.node.innerHTML = xhr.responseText;
xhr.node.style.display = "block";
}
}
xhr.send(post);
}
now when i check this in firebug i can see that the div element is created and appended to the categories element and its display is set to hidden. also the ajax post requests are being sent and the response is being received as expected. But the innerHTML property of div is not set and neither its display is set to block.
This means that the function getLinks loses the div reference.
when i type console.log(div) in the firefox console it says ReferenceError: div is not defined.
can somebody explain whats going on here?
in response to Franks's comment i changed readystate to readyState and i am able to attach the response of the last ajax request to the dom. so that makes it obvious that the div reference is being lost.
Thats because you are using a public (global) variable div that keeps getting overwritten.
Try this in your for loop:
for (i = 0; i < categories.length; i++) {
var link = categories[i].children[1].children[0].attributes['href'].nodeValue;
var div = document.createElement('div'); //USE var!
div.className = "books";
div.style.display = "none";
categories[i].appendChild(div);
getLinks(link, div);
}
Remember that the response handlers innards aren't "fixated" when the callback is defined, so the 'current' value of the div var doesn't get embedded into the function's definition. It'll only be resolved when the function actually executes, by which time it might have been set to some completely other div, or been reset to null as the parent function's scope has been destroyed.
You could store the div value as a data attribute on the xhr object, which you can then retrieve from within the callback:
xhr.data('thediv', div);
xhr.onreadystatechange = function () {
if (xhr.readystate == 4) {
div = xhr.data('thediv');
etc....
Ok, you've got a few globals going on that you don't want. Rule of thumb: unless you need to access a variable outside of a function, place var in front of it. Otherwise you'll have data clobbering itself all over the place:
// changed the name to `d` because div seems to already be a global var.
function getLinks(url, d) {
// make xhr a local variable so it won't get re-written.
var request = new XMLHttpRequest();
request.open('POST', 'ebook_catg.php', true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
url = encodeURIComponent(url)
var post = "url=" + url;
request.onreadystatechange = function () {
// when the request was global, this would be false until the last
// request completed
if (request.readyState == 4) {
// since d only exists as a parameter to getLinks, this should
// already be bound when the onreadystatechange is created.
d.innerHTML = request.responseText;
d.style.display = "block";
}
}
request.send(post);
}
So, why did I just do such strange, strange things? Well, it looks like div was being assigned as a global variable and while JS should always look to function parameter name for binding, we want to eliminate all possible problems. So I changed the name of that variable. Then I set xhr to reflect a local variable with the var keyword. I also changed the name to request. Once again, it shouldn't matter -- var means that the variable will be bound to that scope, but the change is harmless and since I don't know what else you have, I decided to remove ambiguities. If it does not help JS, it will at least help the reader.
NOTE:
The important part of the above answer is var in front of request.
here i am answering my question.The following code works,i mean the response from each post is appended to the corresponding div element.
var xhr=new Array();
window.onload=function() {
var categories=document.getElementById('categories').children;
for(i=0;i<categories.length;i++)
{
var link=categories[i].children[1].children[0].attributes['href'].nodeValue;
var div=document.createElement('div');
div.className="books";
div.style.display="none";
categories[i].appendChild(div);
getLinks(link,div,i);
}
}
function getLinks(url,div,i)
{
xhr[i]=new XMLHttpRequest();
xhr[i].open('POST','ebook_catg.php',true);
xhr[i].setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
url=encodeURIComponent(url)
var post="url="+url;
xhr[i].node=div;
xhr[i].onreadystatechange=function() {
if(xhr[i].readyState==4)
{
xhr[i].node.innerHTML=xhr[i].responseText;
xhr[i].node.style.display="block";
}
}
xhr[i].send(post);
}
i am not marking it as accepted because i still dont understand why i need to use an array of xhr since a local xhr object should be enough because each time the onreadystate function executes it has the reference of the xhr object. Now since javascript functions are also objects therefore every instance of onreadystate function should have its own reference of xhr object and therefore i shouldnt need to create an array of xhrs.
please correct me if i am wrong here

Categories

Resources