Expanding and collapsing table row (tr) using jquery
Asked Answered
M

7

7

enter image description here

I have a table with active and inactive items. This table is dynamically populated from database. I am trying to add a toggle for only inactive items in my table and display all active items. I mean I want to show all active items and slide toggle only inactive items in my table.

<div class="alertsList">
    <table width="100%">
        <tbody>
            <tr>
                <th></th>
                <th>Id</th>
                <th>From</th>
                <th>Action</th>
                <th>State</th>
                <th>time</th>
                <tr class="alertRow">
                    <td></td>
                    <td>1025973</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="Active">Active</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="Active">Active</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="InActive">InActive</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="InActive">InActive</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="Active">Active</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
        </tbody>
    </table>
</div>

$('.alertRow.InActive').Parent.click(function () {

    $(this).nextUntil('tr.td.InActive').slideToggle(1000);
});

My Fiddle code

How can I do that..?

Melville answered 29/9, 2014 at 19:54 Comment(1)
Just checking if you have considered a simpler (and slightly foolish) solution. Is it an option to handle these as two separate grids? Both working independently? That way, your toggle function will reduce to a hide and show kind of an operation. Active grid will always remain seen, and you hide and show the Inactive ones separately. All you need to make sure is that styles are applied so that column widths remain the same for both the grids. Apologies if this is too simplistic a solution.Rutter
T
6

Here's the code to toggle the table rows which are "inactive". It's better to rely on "closest()" than "parent()" or "parents()", because it's faster and it makes more sense to use it in this case.

$(document).ready(function() {

  // Save all the inactive rows
  var inactive_rows = '';
  $('.InActive').closest("tr").each(function() {
    inactive_rows += '<tr class="alertRow">';
    inactive_rows += $(this).html();
    inactive_rows += '</tr>';
  });
  console.log(inactive_rows);
  // Save all the active rows
  var active_rows = "";
  $('.Active').closest("tr").each(function() {
    active_rows += '<tr class="alertRow">';
    active_rows += $(this).html();
    active_rows += '</tr>';
  });
  // Empty the table
  $('.alertsList').html("");
  // Load the new table
  var table_html = '<table width="100%"><thead><th></th><th>Id</th><th>From</th><th>Action</th><th>State</th><th>time</th></thead><tbody>';
  table_html += active_rows;
  table_html += inactive_rows;
  table_html += '</tbody></table><a href="" class="toggleInactiveRows">Toggle Inactive Rows</a>';
  $('.alertsList').append(table_html);

  // Hide the inactive rows when the page loads
  $('.InActive').closest("tr").hide();

  // Toggle the inactive rows
  $('.toggleInactiveRows').click(function() {
    $('.InActive').closest("tr").slideToggle();
    return false;
  });

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="alertsList">
  <table width="100%">
    <thead>
      <th></th>
      <th>Id</th>
      <th>From</th>
      <th>Action</th>
      <th>State</th>
      <th>time</th>
    </thead>
    <tbody>
      <tr class="alertRow">
        <td></td>
        <td>1025973</td>
        <td>SYSTEM</td>
        <td>false</td>
        <td class="Active">Active</td>
        <td>2014-09-23T00:59:26.92</td>
      </tr>
      <tr class="alertRow">
        <td></td>
        <td>1025974</td>
        <td>SYSTEM</td>
        <td>false</td>
        <td class="Active">Active</td>
        <td>2014-09-23T00:59:26.92</td>
      </tr>
      <tr class="alertRow">
        <td></td>
        <td>1025974</td>
        <td>SYSTEM</td>
        <td>false</td>
        <td class="InActive">InActive</td>
        <td>2014-09-23T00:59:26.92</td>
      </tr>
      <tr class="alertRow">
        <td></td>
        <td>1025974</td>
        <td>SYSTEM</td>
        <td>false</td>
        <td class="InActive">InActive</td>
        <td>2014-09-23T00:59:26.92</td>
      </tr>
      <tr class="alertRow">
        <td></td>
        <td>1025974</td>
        <td>SYSTEM</td>
        <td>false</td>
        <td class="Active">Active</td>
        <td>2014-09-23T00:59:26.92</td>
      </tr>
    </tbody>
  </table>
  <a href="" class="toggleInactiveRows">Toggle Inactive Rows</a>
</div>
Tenedos answered 2/10, 2014 at 14:58 Comment(4)
Any chance to Hide Inactive items by default and toggle them?Melville
Here you go :) I edited the code above. Please mark this answer as valid if you feel it solved your issue.Tenedos
Thank you so much but As per my diagram in question InActive items are separated as a block. I am also trying to group by InActive items as separate block and toggle them.Melville
It's better to do that using a server-side scripting language (PHP, ASP.NET etc.). You'll sort the array properly and then you loop over the array. I edited the JavaScript code with a solution to the new problem, but it's better to do it using a server-side language.Tenedos
H
5

First of all, Jquery doesn't properly handle height animations on table elements, So we'll inject <div>'s inside the <td>'s to be animated, as mentioned in this answer.

Secondly, Since you want to toggle the inactive items as a group, We should group them together.

  • We'll find the inactive rows using has() method, wrap them in a <tfoot> using wrapAll() and insert it before the <tbody>, for which we use insertBefore().

    I'm using insertBefore() since it is the better practice due to the advantage mentioned in this answer. We can use insertAfter() as well, since it is semantically okay to include the <tfoot> after the <tbody>,

  • Then we find the <td>'s inside them and wrap their content in a hidden <div> using wrapInner() method.

    You can avoid this step by populating the table accordingly in serverside

  • We then add the option for toggling before the first inactive row.

  • on click of the toggle option, we use closest() to access it's parent <tr> and then nextAll() to target all the following <tr> - which are inactive. We then find the <div's which we injected inside them and call slideToggle() for the toggle functionality.

    We can use parent() as well for accessing the parent <tr> according to current markup. I'm using closest so that it'll work event if you bind the event handler to an element inside <td> - say for example, a button.

    We can use siblings() as well for accessing the following <tr>'s, since we separated the active and inactive rows into different containers.

    Note that we need to delegate the event handler using on() since we're injecting the toggle option dynamically.

  • Finally we use the CSS pseudo element ::before to display the corresponding icon based on a class, which is toggled when the user clicks the toggle option using toggleClass()

Other than these,

  • I've wrapped the title columns inside <thead> which is more semantically appropriate.
  • I've set a minimum width for the <td> to avoid the "jerk" due to the change in width of State column when the visibility of all Inactive items are toggled
  • I've removed the empty <td> and added some CSS for the sake of demo

We end up with the following semantically cool structure which is collapsible:

 <table>
   <thead>
     <tr>
          <th> title</th>
     </tr>
   </thead>
   <tfoot>
    <tr>
      <td>Toggle Switch</td>
    </tr>
    <tr>
      <td><div> Collapsible content</div></td>
     </tr>
   </tfoot>
   <tbody>
     <tr>
          <td>body content</td>
     </tr>
   </tbody>
</table>

$('.alertRow').has(".InActive")
// group them together in a tfoot and insert it before tbody
         .insertBefore(".alertsList table tbody").wrapAll("<tfoot></tfoot>")
// inject the hidden divs inside the columns for animating
         .find("td").wrapInner("<div style='display:none'/>").end()
// insert the toggle option before the first inactive row
         .first().before("<tr><td class='toggle' colspan='5'> Inactive Items</td></tr>");

$(".alertsList table").on("click", ".toggle", function () {
   // following class keeps track of the cuurrent state
   $(this).toggleClass("expanded")
   // find all the injected divs and toggle them
      .closest("tr").nextAll("tr").find('td  div').slideToggle(700);
});
*{
  margin:0;
  padding:0;
}
th,td{
  min-width:80px;
  text-align:center;
}
.toggle{
  background:dodgerblue;
}
.toggle::before{
    content:"+"
}
.toggle.expanded::before{
    content:"-"
}
tfoot tr{
  background:#eee;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="alertsList">
    <table width="100%">
        <thead>
          <tr>
                <th>Id</th>
                <th>From</th>
                <th>Action</th>
                <th>State</th>
                <th>time</th>
            </tr>
        </thead>
        <tbody>
            <tr class="alertRow">
                <td>1025973</td>
                <td>SYSTEM</td>
                <td>false</td>
                <td class="Active">Active</td>
                <td>2014-09-23T00:59:26.92</td>
            </tr>
            <tr class="alertRow">
                <td>1025974</td>
                <td>SYSTEM</td>
                <td>false</td>
                <td class="Active">Active</td>
                <td>2014-09-23T00:59:26.92</td>
            </tr>
            <tr class="alertRow">
                <td>1025974</td>
                <td>SYSTEM</td>
                <td>false</td>
                <td class="InActive">InActive</td>
                <td>2014-09-23T00:59:26.92</td>
            </tr>
            <tr class="alertRow">
                <td>1025974</td>
                <td>SYSTEM</td>
                <td>false</td>
                <td class="InActive">InActive</td>
                <td>2014-09-23T00:59:26.92</td>
            </tr>
            <tr class="alertRow">
                <td>1025974</td>
                <td>SYSTEM</td>
                <td>false</td>
                <td class="Active">Active</td>
                <td>2014-09-23T00:59:26.92</td>
            </tr>
        </tbody>
    </table>
</div>
Hecklau answered 3/10, 2014 at 10:59 Comment(2)
Wissam solution is what I am looking for, but your code is awesome and the way you explain is really useful. I am not sure how to add bonus points to your answer but have to add tick to Wissam for giving solution which I require.Melville
@Chandana You can see the bonus point number below the votes aside every answer, clicking it will give the bonus points to that answer... :) BTW, if you don't do it manually, the accepted answer will get the bonus points when the bounty expires..Hecklau
S
4

You had a couple problems with your selectors:

$('.alertRow .InActive').parent().click(function () {
    $(this).slideToggle(1000);
});

This should work the way you intended.

Unfortunately, as far the animation of the slideToggle is concerned, tables tend to have a restricted style to them. In this case the slideToggle will not animate due to the line-height of the tr elements. You could set the tr line-height and height properties manually in the CSS, but it might cause you some other formatting pain in the future. Here's an example fiddle.

Sacking answered 29/9, 2014 at 20:13 Comment(1)
Toggle is little difficult as items are hidden on click. Could you please help me with tips how can I make active and inactive 2 blocks and toggle inactive items.Melville
B
4

As mentioned in a previous post, you will need to set the line-height and height style properties of the tr tag for the animation effects to work.

Here is an example that toggles all 'InActive' rows using a button.

CSS:

tr {
    height: 15px;
    line-height: 0px;
}

HTML:

<div class="alertsList">
<table width="100%">
    <tbody>
        <th></th>
        <th>Id</th>
        <th>From</th>
        <th>Action</th>
        <th>State</th>
        <th>time</th>
        <tr class="alertRow">
            <td></td>
            <td>1025973</td>
            <td>SYSTEM</td>
            <td>false</td>
            <td class="Active">Active</td>
            <td>2014-09-23T00:59:26.92</td>
        </tr>
        <tr class="alertRow">
            <td></td>
            <td>1025974</td>
            <td>SYSTEM</td>
            <td>false</td>
            <td class="Active">Active</td>
            <td>2014-09-23T00:59:26.92</td>
        </tr>
        <tr class="alertRow">
            <td></td>
            <td>1025974</td>
            <td>SYSTEM</td>
            <td>false</td>
            <td class="InActive">InActive</td>
            <td>2014-09-23T00:59:26.92</td>
        </tr>
        <tr class="alertRow">
            <td></td>
            <td>1025974</td>
            <td>SYSTEM</td>
            <td>false</td>
            <td class="InActive">InActive</td>
            <td>2014-09-23T00:59:26.92</td>
        </tr>
        <tr class="alertRow">
            <td></td>
            <td>1025974</td>
            <td>SYSTEM</td>
            <td>false</td>
            <td class="Active">Active</td>
            <td>2014-09-23T00:59:26.92</td>
        </tr>
    </tbody>
</table>
<input id="btnShowHide" type="button" value="Show/hide" />

JS:

 $('#btnShowHide').click(function () {
    $('.InActive').parent('tr').slideToggle(1000);
});
Bedlamite answered 2/10, 2014 at 0:30 Comment(0)
M
3

A simple solution for this would be, Bind the data in a fashion that the active ones comes first and then the inactive ones (use sorting for the same).

If it is not possible, then probably you can use below code for sorting them (FYI this would work only for those elements which are already present in the DOM and should be run only once)

// a simple compare function, used by the sort below
var compare_rows = function(a, b) {
    var a_val = $(a).find('td.Active').text().toLowerCase();
    var b_val = $(b).find('td.InActive').text().toLowerCase();
    if (a_val > b_val) { return 1; }
    if (a_val < b_val) { return -1; }
    return 0;
};
// the actual sort
$('#tableID tr').sort(compare_rows).appendTo('#tableID');

Then add the Icon for collapsing and Expanding Hidden ones

$("<tr class='toggle'><td colspan='5'>Show Hidden Icons</td></tr>").insertBefore($($(".alertsList table tr td.InActive")[0]).parent())

At last, add the click even for the dynamic icon added as below,

$(".toggle").click(function(){
    $(this).nextAll().slideToggle(1000);
});

Hope this helps :)

Madder answered 6/10, 2014 at 13:34 Comment(0)
M
3

As T J said in his answer, it is complicated to animate the height of a table row (see this answer or this answer for hacks around it, which involve wrapping each cell's content in a <div>).

You could skip the animation altogether, and simply hide or show the inactive rows :

$('#btnToggle').click(function() {
    $('td.InActive').closest('tr').toggle();
});

If you want some visual candy to signal the hiding to the user, I would suggest using .fadeToggle() which will work straight away :

$('#btnToggle').click(function() {
    $('td.InActive').closest('tr').fadeToggle(1000);
});

fiddle


As an extra note : if you can modify the code that generates the table (e.g. : the PHP or ruby (or wathever) code), try putting the active / inactive class on the row (the <tr> node) rather than the inner cell (the <td> node) :

<div class="alertsList">
    <table width="100%">
        <tbody>
            <tr>
                <th></th>
                <th>Id</th>
                <th>From</th>
                <th>Action</th>
                <th>State</th>
                <th>time</th>
            </tr>
            <tr class="alertRow active">
                ...
            </tr>
            <tr class="alertRow active">
                ...
            </tr>
            <tr class="alertRow inactive">
                ...
            </tr>
            <tr class="alertRow inactive">
                ...
            </tr>
            <tr class="alertRow active">
                ...
            </tr>
        </tbody>
    </table>
</div>

This would make for easier rules in your css and js :

//css
tr.inactive {font-style: italic; background-color: #eee}

//js
$('tr.inactive').fadeToggle(1000);
Mede answered 7/10, 2014 at 8:7 Comment(0)
L
3

I create this using fadeToggle:

$("table tr td.InActive").each(function(){
    $(this).parent().children(":not(:first-child)").hide();
});

$('a').click(function () {
    $(this).text() == "+" ? $(this).text("-") : $(this).text("+");
    $(this).parents("tr").children(":not(:first-child)").fadeToggle();
});
a{
    text-decoration: none;
}
a:hover{
    text-decoration: underline;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="alertsList">
    <table width="100%">
        <tbody>
            <tr>
                <th></th>
                <th>Id</th>
                <th>From</th>
                <th>Action</th>
                <th>State</th>
                <th>time</th>
                <tr class="alertRow">
                    <td></td>
                    <td>1025973</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="Active">Active</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="Active">Active</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td><a href="#">+</a></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="InActive">InActive</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td><a href="#">+</a></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="InActive">InActive</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
                <tr class="alertRow">
                    <td></td>
                    <td>1025974</td>
                    <td>SYSTEM</td>
                    <td>false</td>
                    <td class="Active">Active</td>
                    <td>2014-09-23T00:59:26.92</td>
                </tr>
        </tbody>
    </table>
</div>

Aa you can see i added a +/- a element to show/hide inactive rows.

Laforge answered 7/10, 2014 at 20:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.