0

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?

4 Answers 4

3

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

0

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"].

Comments

0

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();
            });
    }
},

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.