进行并行ajax请求

i have javascript code that does these things in a loop

  1. create a div element,append it to the dom and get its reference
  2. pass this reference to a function that makes an ajax post request
  3. 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