views:

69

answers:

2

In the code example below the success callback function logs 'input#04.update' four times rather than each individual input, which makes sense seeing how closures work but how would I go about targeting each individual input using this.

<input type="text" name="" id="01" class="update">
<input type="text" name="" id="02" class="update">
<input type="text" name="" id="03" class="update">
<input type="text" name="" id="04" class="update">

function updateFields(){
 $('input.update').each(function(){
    $this = $(this);
    $.ajax({
      data: 'id=' + this.id,
      success: function(resp){
       console.log($this);
          $this.val(resp)
      }
    });
  });
}
+10  A: 

You forgot var

var $this = $(this);

Don't forget var. One programmer who forgot var went to bed at night and woke up to find his apartment on fire. He added var and the fire went out. Another programmer left var out completely shortly before leaving on a business trip to Europe. The airplane developed in-flight mechanical problems shortly after takeoff, causing the pilot to initiate emergency landing procedures. From his laptop the programmer quickly added var and the plane made it safely to an airport.

Don't forget var. If you put var in your code, you'll meet somebody special today. Try it. It sounds amazing but it really works!

Pointy
That's amazing... but... could you tell me a technical reason to do that? I mean, I sometimes forget to add `var`... and I haven't had problems. But your words are scary, and from now on I won't forget to add `var`. Anyway... do you have a technical explanation for this kind of problems?
Cristian
The reason: your `success` handler, anonymous function references a `$this` variable that is global. So every one of the 4 instances of this anonymous function references the same variable and therefore the same value, since they are executed long after the `each` function has returned.
Alsciende
@Cristian - Without the `var`, it's a global variable you're updating each loop, not local to this closure's scope which you want it do be in this case.
Nick Craver
Nick's right - without an explicit `var`, not only will your love life fall apart completely, but your variables will all be global.
Pointy
Cristian
@Point - DOH! I'm so stupid at times. Well most of the time but that really was a beginner's mistake. Still, that was possibly the best response I've ever had to a question. Thank you so much.
Nick Lowman
+3  A: 

Pointy's correct on var usage, another alternative is to use $.proxy(), like this:

function updateFields(){
 $('input.update').each(function(){
    $.ajax({
      data: 'id=' + this.id,
      success: $.proxy(function(resp){
                 $(this).val(resp);
               }, this)
    });
  });
}

This closure creator will make this refer to the input element when you're inside the success callback, which is usually what you're after...so I'm not sure why this isn't the case by default, but in any case $.proxy() rectifies the situation.

Nick Craver
+1 for making me read up on `$.proxy()`.
patrick dw
`$.proxy()` is nice but I wish it also supported the "parameter stuffing" or fake currying that Prototype's `.bind` supports. Now I'll have to check to see if the new ECMAScript 5 `.bind` is like Prototype's.
Pointy
@Pointy - The second `$.proxy()` approach can do some mutation like this depending on how you structure it...but yeah it's not up to par prototype, but I think it's not really missing a lot...I think the 2 libraries just take a much different approach to binding and iteration with respect to closures, jQuery tends to create more for you along the way, for better or worse.
Nick Craver
@Nick Craver - +1 - Thanks man. That's a great idea and one I wouldn't have considered.
Nick Lowman
@Pointy - what do you mean by 'parameter stuffing' or 'fake currying'?
Nick Lowman
Well it's a trick, like what `$.proxy` does for binding the "this" pointer. In addition, some libraries (like Prototype and Functional, and maybe Underscore) also let you take a function and generate a *new* function that will always supply some particular arguments to the original function. It's like, if you wanted something like the jQuery `attr()` function that *always* returned the "id", you could make a function that always called `attr('id')` and use it. It's a way of doing things that's common in the functional programming world (Scheme, Haskell, etc)
Pointy