3

Hoping someone can help me overcome my Javascript ignorance.

I've got a form that includes checkboxes and I've got a piece of JS that toggles selecting/deselecting all the boxes. And so far, it all works as expected.

The wrench in the works is that I've got multiple groups of checkboxes in this form and I would like to select/deselect by group, not all the checkboxes in the form. This is a sample of the php and html. As you can see, the form is in a table and there is a checkbox in the header row that performs the action. 'resources_req' is the name of the checkbox element in the form

<form method="post" name="add_reservation"> 
<?php for($x=0; $x<count($groups); $x++) : // make seperate display for each group ?>
    <div class="group_<?php echo $group_label; ?>">
    <table class="res">
        <tr>
<!-- form: checkbox all -->
    <?php if($make_res == 'enter') : // adds checkbox to check all ?>
            <th><input type="checkbox"  onClick="toggle(this, 'resources_req[]')" /></th>
    <?php endif; ?>
<!-- end form: checkbox all -->
        </tr>
...
foreach($resources as $resource) { // for each resource/laptop
    $form_start = '<td>';
        $form_start .= '<input type="checkbox" name="resources_req[]" value="'.$resource['id'].'"';
        $form_start .= ' />';
    $form_start .= '</td>';
}
...
    </table>
    </div>
<?php endfor; // loop for each group ?>
<input type="submit" name="add_reservation" value="Make this reservation"  />
</form> 

Here is the JS being called:

function toggle(source, element) {
    checkboxes = document.getElementsByName(element);
    for(var i in checkboxes)
        checkboxes[i].checked = source.checked;
}

Best I can put together, the 'this' in the script call is referring to the form. I thought if maybe I put each of these groups in to their own div class, I could then somehow refer to just that but now I'm just lost. Any help or suggestions appreciated!

EDIT: I asked for suggestions and it's been suggested I post only the html:

<form method="post" name="add_reservation"> 
    <div class="group_A">
        <table>
            <tr>
                <th><input type="checkbox"  onClick="toggle(this, 'resources_req[]')" /></th>
                <th>Name</th>
            </tr>
            <tr>
                <td><input type="checkbox" name="resources_req[]" value="1" /></td>
                <td>John</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="resources_req[]" value="2" /></td>
                <td>Bill</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="resources_req[]" value="3" /></td>
                <td>Fred</td>
            </tr>
        </table>
    </div>
    <div class="group_b">
        <table>
            <tr>
                <th><input type="checkbox"  onClick="toggle(this, 'resources_req[]')" /></th>
                <th>Name</th>
            </tr>
            <tr>
                <td><input type="checkbox" name="resources_req[]" value="4" /></td>
                <td>George</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="resources_req[]" value="5" /></td>
                <td>Tom</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="resources_req[]" value="6" /></td>
                <td>Raymons</td>
            </tr>
        </table>
    </div>
    <input type="submit" name="add_reservation" value="Make this reservation"  />
</form> 
user113716
  • 318,772
  • 63
  • 451
  • 440
Stace
  • 147
  • 1
  • 2
  • 9
  • 1
    I don't know what anyone else thinks, but as far as I'm concerned, it would be so much easier to help if I didn't have to try to translate your PHP into the HTML it renders. – user113716 Jun 20 '11 at 23:27
  • @patrick-dw: followed your suggestion; see post edit – Stace Jun 21 '11 at 01:02
  • Just a suggestion: You might find jQuery very useful when doing this sort of stuff. – Chetan Jun 21 '11 at 01:17

2 Answers2

3

I changed a few things:

First, instead of passing the value of name, I'm passing the tagName of 'input' instead.

<input type="checkbox"  onClick="toggle(this, 'input')" />

Then in the toggle() function, I select the parentNode of the source element, and do a getElementsByTagName() so that I only get the input elements in the local div.

Also, I changed the for-in loop to a standard for loop, which is the proper type of loop to iterate over indexed elements. The for-in can actually give some problems.

function toggle(source, element) {
    var checkboxes = source.parentNode.getElementsByTagName(element);
    for (var i = 0; i < checkboxes.length; i++) {
        checkboxes[i].checked = source.checked;
    }
}

Live Example: http://jsfiddle.net/37mT2/


Alternatives:

Instead of parentNode, select the ancestor <div> element by assigning it an ID, and passing it to your toggle() function.

<input type="checkbox"  onClick="toggle(this, 'input', 'someUniqueId_1')" />

<input type="checkbox"  onClick="toggle(this, 'input', 'someUniqueId_2')" />

function toggle(source, element, id) {
    var checkboxes = document.getElementById( id ).getElementsByTagName('input');
    for (var i = 0; i < checkboxes.length; i++) {
        checkboxes[i].checked = source.checked;
    }
}

Or you could traverse up the parent nodes until you reach your first <div> element:

<input type="checkbox"  onClick="toggle(this, 'input')" />

function toggle(source, element) {

    while( source && source = source.parentNode && source.nodeName.toLowerCase() === 'div' ) {
        ; // do nothing because the logic is all in the expression above
    }
    var checkboxes = source.getElementsByTagName('input');

    for (var i = 0; i < checkboxes.length; i++) {
        checkboxes[i].checked = source.checked;
    }
}

Or you could give the <div> elements at that level a common class name and traverse up the parent nodes until you reach that class. In the code below, your <div> elements class is "someClassName":

<input type="checkbox"  onClick="toggle(this, 'input')" />

function toggle(source, element) {

    while( source && source = source.parentNode && source.className === 'someClassName' ) {
        ; // do nothing because the logic is all in the expression above
    }
    var checkboxes = source.getElementsByTagName('input');

    for (var i = 0; i < checkboxes.length; i++) {
        checkboxes[i].checked = source.checked;
    }
}

EDIT: Fixed a typo. I had getElementsById instead of getElementById.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • @patrick-dw: Thanks very much for the ideas - I think this is probably what I was looking for without knowing it. But I'm still having trouble making it work, probably because, in my oversimplifying the html, I left out the table elements. I'll update that to reflect but I think the parentNode is keying on that, instead of the local div. Can I just name the local div? – Stace Jun 21 '11 at 14:43
  • @Stace: Yes, you would need to give each local `
    ` an ID, and pass that ID to your `toggle()` function, then use `document.getElementById()` to select it based on its ID. (IDs must be unique on the page.) An alternative is to traverse up the parent nodes until you get to a `
    ` element. This will work as long as there isn't an intermediate `
    ` that you don't want to select. If there is such, then you could give the `
    ` a class, and look for that. I'll update with each of these solutions.
    – user113716 Jun 21 '11 at 14:50
  • @patrick-dw: Wow, that was fast! I just updated my post with to add the table elements. Seems like specifying the div might be the way to go, instead of traversing up. In the real world, this is all generated in a loop and the div classes are being generated dynamically anyway (see near the top of my code). Thanks! – Stace Jun 21 '11 at 14:55
  • @Stace: Yes, if you can easily give each one a unique ID on the server side, that would be simplest. I updated with 3 more examples. The first assumes a unique ID that you pass to `toggle()`. The second traverses up until a `div` element is reached, and the third probably won't work because you already have a different class name on each `div`. I could make it work, but it sounds like one of the other two solutions would be better. – user113716 Jun 21 '11 at 15:03
  • @Stace: I fixed a typo where I misspelled the `getElementById` function name. – user113716 Jun 21 '11 at 15:14
  • @patrick-dw: thanks for all the great examples! I went with the first one because it seemed to make the most sense for my needs (everything is in a php loop anyway). Thanks for all of your help and patience! – Stace Jun 21 '11 at 22:26
  • You're welcome @Stace. In order to close out this Q & A, please remember to click the large checkmark to the left of one of the answers you received. Thanks. :o) – user113716 Jun 22 '11 at 00:19
0

Best I can put together, the 'this' in the script call is referring to the form. I thought if maybe I put each of these groups in to their own div class, I could then somehow refer to just that but now I'm just lost. Any help or suggestions appreciated!

http://jsfiddle.net/JG4uf/

JavaScript Loops: for...in vs for

Community
  • 1
  • 1
RobertO
  • 2,655
  • 1
  • 20
  • 18