Question

I have a table that's generated by a normal PHP loop. What I want to do is create a form in the first column of each row that's hidden by default but appears when you click a toggle link in that row.

I can make a normal toggle-able div by creating a CSS id called hidden and setting display: none;. Unfortunately I can't keep creating divs with id=hidden that are automatically associated with the preceding link.

I am pretty inexperienced with both Javascript and CSS, so I've mostly tried to do this by patching together examples but I'm coming up empty. I've read in some places that you can't put divs inside of a table, so maybe I'm going about this all wrong.

Here's an example of what the code does and how I wish it worked, but of course it does not.

<script language="JavaScript" type="text/javascript">
    function toggle(id) {
        var state = document.getElementById(id).style.display;
            if (state == 'block') {
                document.getElementById(id).style.display = 'none';
            } else {
                document.getElementById(id).style.display = 'block';
            }
        }
</script>


<?php

while($array = mysql_fetch_array($sql))
    {
?>
<tr>
    <td>
<?php
        echo $array['some_data'];
?>
        <a href="#" onclick="toggle('hidden');">Toggle</a>
        <div id="hidden"><?php echo $array['hidden_thing']; ?></div>
    </td>
    <td>
        <?php echo $array['some_other_data']; ?>
    </td>
</tr>
<?php
    }
?>
Was it helpful?

Solution

Just use a different ID for each row:

<?php
$count = 0;
while($array = mysql_fetch_array($sql)) {
  $id = 'hidden' . $count++;
  $data = $array['some_data'];
  $hidden = $array['hidden_thing'];
  $other_data = $array['other_data'];
  echo <<<END
<tr>
  <td>$data <a href="#" onclick="toggle('$id');>Toggle</a>
    <div id="$id">$hidden_thing</div>
  </td>
  <td>$other_data</td>
</tr>

END;
}

OTHER TIPS

Make it a span instead of a DIV as I think that some browsers don't support divs inside table elements. Also, instead of referring to it by ID, pass in this.nextSibling() to the toggle, using DOM navigation to get the next sibling (which should be the SPAN) to show/hide.

  function toggle(ctl) {
      var state = ctl.style.display;
      if (state == 'block') {
          document.getElementById(id).style.display = 'none';
      } else {
          document.getElementById(id).style.display = 'block';
      }
  }


  <a href="#" onclick="toggle(this.nextSibling);">Toggle
  </a><div><?php echo $array['hidden_thing']; ?></div>

EDIT: As @tomhaigh suggests (and as shown in the example), for this to work you need to make sure that there is no text/whitespace between the anchor and the div. You could also write a function that, given a DOM element, would select the next non-text DOM element and return it. Then pass this to that function and the result to your toggle function.

Here's my recommended (general solution) using jQuery to reference events relatively instead of maintaining ids for each row and form. This also allows you to hide non-active row forms easily, which is a good idea since only one form can be submitted at a time.

HTML:

<table id="tableForms" class="table">
  <tr>
    <td class="rowForm"><form><span>form1 content</span></form></td>
    <td class="showRowForm">click on row to show its form</td>
    </tr>
  <tr>
    <td class="rowForm"><form><span>form2 content</span></form></td>
    <td class="showRowForm">click on row to show its form</td>
    </tr>
  <tr>
    <td class="rowForm"><form><span>form3 content</span></form></td>
    <td class="showRowForm">click on row to show its form</td>
    </tr>
</table>

Javascript:

<script type="text/javascript" src="/assets/js/jquery.min.js"></script>
<script type="text/javascript">
//as soon as the DOM is ready, run this function to manipulate it
$(function() {
    // get all tr elements in the table 'tableForms' and bind a 
    // function to their click event
    $('#tableForms').find('tr').bind('click',function(e){
        // get all of this row's sibblings and hide their forms.
        $(this).siblings().not(this).find('td.rowForm form').hide();

        // now show the current row's form
        $(this).find('td.rowForm form').show();
    }).
    // now that the click event is bound, hide all of the forms in this table
    find('td.rowForm form').hide();
});
</script>

Demo:

A working demo of this can be found here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top