views:

382

answers:

4

Several related questions already exist on this site and elsewhere, but none of the answers seems to apply to my situation. I have a bunch of radio buttons in HTML.

<input type="radio" name="b1" value="aa"> aa<br>
<input type="radio" name="b1" value="ab"> ab<br>
<input type="radio" name="b1" value="ac"> ac<br>

<input type="radio" name="b2" value="ba"> ba<br>
<input type="radio" name="b2" value="bb"> bb<br>
<input type="radio" name="b2" value="bc"> bc<br>

<script type="text/javascript">
var PK = {    
   modes: ["b1", "b2"],

   init: function() {        
      // attach actions to radio buttons
      for (var key in this.modes) {
          var mode = this.modes[key];
          $("input[name='" + mode + "']").bind(
               "change", 
               function() { 
                 PK[mode]($("input[name='" + mode + "']:checked").val()); 
               } 
          );
       }
    },

    b1: function(val) { .. do something .. },

    b2: function(val) { .. do something else .. },
};

$(document).ready(function() {
   PK.init();
});
</script>

The above doesn't work at all. I have tried .live instead of .bind, and even that doesn't work as expected. In fact, both of them bind the action to whatever is the last entry in modes, in the above, "b2". That is, the function PK.b2() is fired whether I change the value in b1 or b2 buttons. If I reverse the modes entries, the last one takes on the value. How can I accomplish he above successfully?

+1  A: 

In the for loop in your code the variable mode is set each time for the the different modes. However it will only keep the last value of the loop. so instead you need to make some changes to the function inside the bind.

$("input[name=" + mode + "]").bind(
    "change", 
    function(e) {
        PK[$(this).attr('name')]($(this).val()); 
    } 
);

Now we are using jquery to figure out the name of the item we are working with, rather than relying on a variable that only has the last bit of data from a loop.

lrudor
+2  A: 

JavaScript closures use the parent function scope, so PK[mode] will always refer to the last mode. You can do something like this to avoid it:

$("input[name=" + mode + "]").bind(
    "change", 
    function(mode) { 
        return function() {
             PK[mode]($("input[name='" + mode + "']:checked").val());
        };
    }(mode)
);

See for example the The infamous loop problem section of this article for more details.

Lukáš Lalinský
A: 

There are two problems from what I see.

First, as lrudor pointed out, mode in the closure is the last mode iterated.

Second, use [name=whatever] instead of [name="whatever"].

strager
A: 

Am I understanding you correct if this is what you want to do?

init: function() {
 // attach actions to radio buttons
 for (var key in this.modes) {
  var mode = this.modes[key];
  $("input[name='" + mode + "']")
   .bind("change", function(e) {
    var radio = e.srcElement;
    PK[radio.name](radio.value);
   })
   .bind("click", function() {
    // Loose focus of the radio button once clicked,
    // so the change-event fires
    this.blur();
   });
 }
},
Kristoffer Deinoff