开发者

jQuery AJAX function can't see my variable

I have the following code, which i've simplified, where $currEl gets logged and displays correctly, but in the $.ajax call it logs as null.

Am I missing something?

for(var i=0, j = atgSlots.length; i < j; i++) {
 var currSlot = atgSlots[i].split('|'),
   $currEl = currSlot[0].length ? $('[data-atg-url=' + currSlot[0] + ']') : null,
   wcmLocation = currSlot[2] || null;

 if ($currEl !== null && wcmLocation !== null) {
  console.log($currEl);
  $.ajax({
   url: wcmLocation,
   success: function(html) { console.log($currEl); updateSlots.setContent($currEl, 开发者_开发百科html); },
   error: updateSlots.checkDefault
  }); // $.ajax
 }
} // for : atgSlots


The problem is that your ajax success function has a live reference to the $currEl variable, not a copy of it as of when the function was created. Consequently, all of those success handlers are referencing the same variable and therefore the same value — the last value assigned to $currEl in the loop. Here's a much, much simplified example of the same effect:

var a;
a = "before";
setTimeout(function() {
    alert(a);
}, 10);
a = "after";

That alerts "after", not "before", because the function that gets called after 10ms uses the current value of a. See it in action.

You can address this by giving each function its own variable to refer to, using an intermediary function. Here's a minimalist change:

for(var i=0, j = atgSlots.length; i < j; i++) {
 var currSlot = atgSlots[i].split('|'),
   $currEl = currSlot[0].length ? $('[data-atg-url=' + currSlot[0] + ']') : null,
   wcmLocation = currSlot[2] || null;

 if ($currEl !== null && wcmLocation !== null) {
  console.log($currEl);
  $.ajax({
   url: wcmLocation,
   // ==== Change starts here
   success: (function($thisEl) {
     return function(html) { console.log($thisEl); updateSlots.setContent($thisEl, html); };
   })($currEl),
   // ==== Change ends here
   error: updateSlots.checkDefault
  }); // $.ajax
 }
} // for : atgSlots

What that does is create and call a factory function on each loop, passing into the function the current value of $currEl. That function then returns the function that should be used as the success handler. The success handler uses other information from the outer context (html, which I'm assuming should be common to all of them), but uses $thisEl (the function argument) instead of $currEl.

It's actually slightly inefficient to do it that way, because we create multiple identical copies of our new factory function. A less minimalist — and perhaps clearer — version would look like this:

for(var i=0, j = atgSlots.length; i < j; i++) {
 var currSlot = atgSlots[i].split('|'),
   $currEl = currSlot[0].length ? $('[data-atg-url=' + currSlot[0] + ']') : null,
   wcmLocation = currSlot[2] || null;

 if ($currEl !== null && wcmLocation !== null) {
  console.log($currEl);
  $.ajax({
   url: wcmLocation,
   success: buildSuccessHandler($currEl),
   error: updateSlots.checkDefault
  }); // $.ajax
 }
} // for : atgSlots

function buildSuccessHandler($thisEl) {
    return function(html) {
        console.log($thisEl);
        updateSlots.setContent($thisEl, html);
    };
}

(I'm assuming all of this code is contained inside a function somewhere.)

More about closures and this live reference thing in this blog post: "Closures are not complicated".


The problem will probably be that when the AJAX callback is called $currEl will be set to a different value, or null, as it will take the value of $currEl when the callback is called, not when the AJAX function is declared.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜