3

I have multiple checkboxes that need to behave as radio buttons. The number of checkbox fields can vary so I tried to use a for loop to go over them.

When I use the following code everything works ok

$("input[name='item_meta[221][0][253][]']").on('click', function() {$("input[name='item_meta[221][0][253][]']").not(this).attr('checked', false)});
$("input[name='item_meta[221][1][253][]']").on('click', function() {$("input[name='item_meta[221][1][253][]']").not(this).attr('checked', false)});
$("input[name='item_meta[221][0][268][]']").on('click', function() {$("input[name='item_meta[221][0][268][]']").not(this).attr('checked', false)});
$("input[name='item_meta[221][1][268][]']").on('click', function() {$("input[name='item_meta[221][1][268][]']").not(this).attr('checked', false)});

However, this would mean that if I have 10 of these checkbox groups, that I would need to write 10 lines for each. So I tried doing the same with a for loop like this:

for (i=0; i < 1; i++){
     $("input[name='item_meta[221][" + i + "][253][]']").on('click', function() {$("input[name='item_meta[221][" + i + "][253][]']").not(this).attr('checked', false)});
     $("input[name='item_meta[221][" + i + "][268][]']").on('click', function() {$("input[name='item_meta[221][" + i + "][268][]']").not(this).attr('checked', false)});
}

But when I use this, it doesn't work?! So what am I missing??

gleysen
  • 33
  • 4
  • The value of `i` in the `click` callback will be the last iteration value (1 in this case) not the value inside the loop (0). See: https://stackoverflow.com/questions/111102/how-do-javascript-closures-work – freedomn-m Aug 10 '20 at 09:38
  • @freedomn in this case i will never be 1 – B''H Bi'ezras -- Boruch Hashem Aug 10 '20 at 09:49
  • It seems like you only want 1 item from a group of common named checkboxes to be selected - if so, why not just use a `radio` input? – Rory McCrossan Aug 10 '20 at 09:52
  • @bluejayke are you sure about that? it continues while `i<1` so therefore breaks when `i>=1` - a simple test will remind you how `for` loops work: `var i;for (i=0; i < 1; i++){ };console.log(i)` – freedomn-m Aug 10 '20 at 09:55
  • @freedomn the code for that test has two {}s immediately following the for loop, with the console. Log statement outside of it completely... Try moving the console. Log inside the brackets and see if you get the same result – B''H Bi'ezras -- Boruch Hashem Aug 10 '20 at 09:57
  • @bluejayke where do you think the click callbacks run? Does this help you? `var i;for (i=0; i < 1; i++){ $(document).click(function() { console.log(i); }); }` – freedomn-m Aug 10 '20 at 10:00
  • @freedom I'm not sure what you're talking about, if you're trying to prove that for loops with I=0 to I< 1 will execute at all, then that is incorrect. The callbacks run in their own context, but they are set up in the for loop. If the for loop is never executed, they will never be created in the first place. Not sure where the controversy is here – B''H Bi'ezras -- Boruch Hashem Aug 10 '20 at 10:04
  • @bluejayke the value of `i` *in the callback* will always be the last iteration value. In OPs case that will be `1` as it's `for(i=0;i<1...` - you stated it will *"never be 1"* - which is clearly and demonstrably incorrect. **in the click callback `i===1`** (for that for loop) and not `0` as expected (and not the value of `i` as expected). Not sure why you've gone off on a tangent about the loop not running at all. – freedomn-m Aug 10 '20 at 10:08
  • @freedomn k clarification: *in the loop* I will never be 1, because the loop will never be called in the first place. Not sure why it's relevant they I will be 1 afterwards, if there is no code afterwards – B''H Bi'ezras -- Boruch Hashem Aug 10 '20 at 10:10
  • @bluejayke *in the loop* it will never be 1 - *in the callback* it will always be 1. The click callback is a callback, which is *run afterwards* - **and this is OPs issue** - so highly relevant. You've even stated this in your own answer: https://stackoverflow.com/a/63337801/2181514 - are you really trolling me on SO? – freedomn-m Aug 10 '20 at 10:15
  • @freedom yes if the loop ran for at least one iteration, then the callbacks will be created and then I will be the last value, but if there are no iterations in the for loop in the first place then there never existed any code expressions that set the callbacks in the first place?? – B''H Bi'ezras -- Boruch Hashem Aug 10 '20 at 10:17
  • So now you're saying `for (i=0; i < 1; i++)` won't even run once? – freedomn-m Aug 10 '20 at 10:31
  • @freedom it won't run the code in it's own {}s , that's what I've been saying the whole time – B''H Bi'ezras -- Boruch Hashem Aug 10 '20 at 10:46

2 Answers2

3

Give it a try with let.

Explanation : The variable i in the code, is a global variable (declared with var). Unlike let, var do not support block scopes. The click event is actually binded when the button is clicked first time, not when the for loop executes. As i was a global variable, it had iterated through the loop and its value had become 10. When the button was clicked, the button was bind to input[name='item_meta[221][10][268][]'] which was not present. That is why, let is used because it supports block scope, and preserves its value for binding event.

for (let j = 0; j < 10; j++) {
  $("input[name='item_meta[221][" + j + "][253][]']").on('click', function() {
    $("input[name='item_meta[221][" + j + "][253][]']").not(this).attr('checked', false)
  });
  $("input[name='item_meta[221][" + j + "][268][]']").on('click', function() {
    $("input[name='item_meta[221][" + j + "][268][]']").not(this).attr('checked', false)
  });
}
Pranav Rustagi
  • 2,604
  • 1
  • 5
  • 18
  • Good answer, just explain it a bit! – Guerric P Aug 10 '20 at 09:44
  • if I use this, it works on item_meta[221][0][253][], but not on item_meta[221][1][253][] – gleysen Aug 10 '20 at 09:51
  • Are you sure that, there is no `i` variable, declared with `var`, in code before that? That might be causing problem. Try using some other variable like `j` or `k` in this for loop in place of `i`. – Pranav Rustagi Aug 10 '20 at 09:59
  • ok, I think this indeed should work, however the second checkboxes (item_meta[221][1][253][]) is loaded after the page has been loaded and I was using the jQuery(document).ready(function and so it only works on the checkboxes that were loaded in the beginning... so I need to see how I can run the code everytime that DOM elements are being added.... I'll check this as a good answer for now ;-) Thx! – gleysen Aug 10 '20 at 10:37
1

You have to use closure, or else you will get the last value of I only. Either use .forEach instead or make an anonymous function call with I as the only parameter, like

for(var i=0;i <8; i++) {
  (function(k) {
    //Do stuff with k as i replacement
  })(i)
}
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • if I use this, it works on item_meta[221][0][253][], but not on item_meta[221][1][253][] – gleysen Aug 10 '20 at 09:56
  • this is the code I used: `for(var i=0;i <10; i++) { (function(k) { $("input[name='item_meta[221][" + k+ "][253][]']").on('click', function() {$("input[name='item_meta[221][" + k + "][253][]']").not(this).attr('checked', false)}); $("input[name='item_meta[221][" + k + "][268][]']").on('click', function() {$("input[name='item_meta[221][" + k + "][268][]']").not(this).attr('checked', false)}); })(i) }` – gleysen Aug 10 '20 at 09:57
  • 1
    @gleysen ok then that must be a problem with that particular input field with that particular name existing in that particular context, which is impossible to troubleshoot without seeing the code for which those input fields were made, – B''H Bi'ezras -- Boruch Hashem Aug 10 '20 at 10:00