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.
精彩评论