i have javascript code that does these things in a loop
div
element,append it to the dom
and get its referencepost
requestinnerHTML
of the passed element referencehere 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.
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 xhr
s. please correct me if i am wrong here