0

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.

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Fat Monk
  • 2,077
  • 1
  • 26
  • 59

1 Answers1

1

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);
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Dhananjaya Kuppu
  • 1,322
  • 9
  • 10
  • Can you explain what is happening in the suggestion? It doesn;t seem to work either and I now get `data.responseText is undefined`. – Fat Monk Nov 17 '16 at 14:45
  • The value of "i" will be undefined because the call back function will execute in a different call stack and has lost the scope of "i". In order to retain the value of i, we need to capture "i" in closure. One way is to use immediately invoked function as created above in second approach. – Dhananjaya Kuppu Nov 17 '16 at 14:55
  • Second example seems to work perfectly, but I am confused by the syntax. I understand the problem with 'i' being out of scope or otherwise unavailable in my original code, but don't understand how `(function(j))...(i)` manages to assign the value of `i` to `j`. And the first example just didn't work for me at all - the `data` that is returned from the AJAX call is being lost somehow. – Fat Monk Nov 17 '16 at 15:05
  • (function(j) { // your code })(i) --- passing "i" as a parameter the value of i is now passed as parameter and bound to a j - variable. "j" is in closure scope and will be available in callback. I have used "j" to avoid ambiguity as "i" is already present in parent scope. – Dhananjaya Kuppu Nov 17 '16 at 15:15
  • But the opening bracket that encloses the function is closed before the brackets that enclose the 'i'.... so it looks like we defined the function within parenthesis, then separately tag (i) on the end. `function(j) {//function code })` then followed by `(i)` on its own. I'm obviously missing something here, and the fact that your code works makes me think it is probably something really useful that I'm missing. – Fat Monk Nov 17 '16 at 15:23
  • function code is wrapped inside parenthesis (function(j){}), that statement is equal to var ex = function(j){}; when you place additional parenthesis (function(j){})(i) you are invoking that function expression by passing i as parameter (function(j){})(i) equals to ex(i). The first () wraps function, second () invokes function. – Dhananjaya Kuppu Nov 18 '16 at 04:35
  • @DhananjayaKuppu Just point **Fat Monk** to an article e.g. [*Self-Executing Anonymous Functions*](http://markdalgleish.com/2011/03/self-executing-anonymous-functions/). – Mr. Polywhirl Nov 18 '16 at 12:00