使用jQuery展开和折叠表格行(tr)

7

在此输入图片描述

我有一个包含活动和非活动项目的表格。这个表格是从数据库中动态填充的。我正在尝试添加一个切换,只显示表格中非活动的项目。即我想要显示所有活动的项目,并滑动切换只显示非活动项目。

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

我的fiddle代码

我该怎么做呢..?


只是想确认一下,你是否考虑过一个更简单(也许有点愚蠢)的解决方案。将它们作为两个独立的网格处理,这是一个选项吗?这样,您的切换功能将减少为一种隐藏和显示操作。活动网格始终可见,而您可以分别隐藏和显示非活动网格。您需要确保应用样式以使两个网格的列宽保持相同。如果这个解决方案太简单了,请原谅。 - ArunGeorge
7个回答

6
这是切换“非活动”表行的代码。最好依赖于“closest()”而不是“parent()”或“parents()”,因为这样更快,并且在这种情况下使用它更有意义。

$(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>


有没有可能默认隐藏不活动的项目并切换它们? - Kurkula
1
这是修改后的代码。如果您认为它解决了您的问题,请将此答案标记为有效。 - Wissam El-Kik
非常感谢,但根据我在问题中的图表,非活动项目被分为一个块。我也试图将非活动项目分组为单独的块并切换它们。 - Kurkula
最好使用服务器端脚本语言(如PHP、ASP.NET等)来完成这个任务。你可以正确地对数组进行排序,然后循环遍历数组。我已经编辑了JavaScript代码并提供了解决新问题的方案,但最好使用服务器端语言来完成它。 - Wissam El-Kik

5
首先,Jquery在表格元素上不能正确处理高度动画,因此我们将在<td>中注入<div>以进行动画处理,正如答案所述。
其次,由于您想要将非活动项目作为一组切换,因此我们应该将它们分组在一起。
我们将使用has()方法找到非活动行,用<tfoot>将它们包含起来,然后使用insertBefore()将其插入到<tbody>之前。

我使用insertBefore(),因为根据这个答案的优势,这是更好的实践。我们也可以使用insertAfter(),因为将<tfoot>放在<tbody>之后在语义上是可以接受的。

然后我们查找其中的<td>并使用wrapInner()方法将它们的内容包装在一个隐藏的<div>中。

您可以通过在服务器端相应地填充表格来避免这一步骤

接下来我们添加切换第一个非活动行之前的选项。 点击切换选项时,我们使用closest()访问其父级<tr>,然后使用nextAll()来定位后面的所有<tr> - 这些都是非活动的。然后我们查找其中注入的<div>并为切换功能调用slideToggle()

我们也可以使用parent()来访问父级<tr>,因为当前的标记结构。我使用closest,这样即使将事件处理程序绑定到<td>内的元素(例如按钮)上,它也能正常工作。

我们也可以使用siblings()来访问后面的<tr>,因为我们将活动行和非活动行分别放在不同的容器中。

请注意,由于我们正在动态注入切换选项,因此需要委派事件处理程序,并使用on()

最后,我们使用CSS伪元素::before根据类显示相应的图标,该类在用户点击切换选项时使用toggleClass()切换。
除此之外,
我已经将标题列包装在语义上更合适的<thead>中。
我设置了<td>的最小宽度,以避免在切换所有Inactive项目的可见性时,State列的宽度变化引起的"抖动"。
我删除了空的<td>并添加了一些CSS以进行演示。
我们最终得到了以下的语义化结构,它是可折叠的。
 <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>


Wissam的解决方案正是我所寻找的,但你的代码也很棒,而且你解释的方式非常有用。我不确定如何给你的答案加分,但必须给Wissam打勾,因为他提供了我需要的解决方案。 - Kurkula
1
@Chandana 您可以在每个答案旁边看到奖励分数,点击它将会给予该答案奖励分数... :) 顺便说一句,如果您不手动操作,当悬赏期满时被接受的答案将获得奖励分数。 - T J
1
++ 非常出色的答案! - user2140173

4

你的选择器有一些问题:

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

这应该按照您预期的方式工作。
不幸的是,就slideToggle的动画而言,表格往往具有受限制的样式。在这种情况下,由于元素的行高,slideToggle将无法进行动画处理。您可以在CSS中手动设置的行高和高度属性,但这可能会在未来给您带来其他格式化问题。这里有一个示例fiddle

Toggle是有点困难的,因为项目在点击时被隐藏了。你能否帮我提供一些提示,如何创建活动和非活动两个块,并切换非活动项目? - Kurkula

4

如之前的帖子中提到,为了使动画效果起作用,您需要设置tr标签的line-heightheight样式属性。

这里是一个使用按钮切换所有“InActive”行的示例。

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

3
我使用 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>

你可以看到,我添加了一个 +/- a 元素来显示/隐藏不活跃的行。

3

一个简单的解决方案是按照一定方式绑定数据,使活跃的数据排在前面,然后再排列不活跃的数据(可以使用排序实现)。

如果不可能实现这一点,那么可以使用以下代码来对它们进行排序(需要注意:此方法仅适用于已经存在于DOM中的元素,且只能运行一次)。

// 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');

然后添加折叠和展开隐藏图标。
$("<tr class='toggle'><td colspan='5'>Show Hidden Icons</td></tr>").insertBefore($($(".alertsList table tr td.InActive")[0]).parent())

最后,按照以下方式为添加的动态图标添加点击事件:
$(".toggle").click(function(){
    $(this).nextAll().slideToggle(1000);
});

希望这有所帮助 :)

3
T J 在他的回答中所说,动画化表格行高度是复杂的(参见这个答案或者这个答案以了解绕过此问题的方法,这些方法涉及将每个单元格的内容包裹在一个<div>中)。
你可以直接隐藏或显示不活动的行,跳过动画化的部分:
$('#btnToggle').click(function() {
    $('td.InActive').closest('tr').toggle();
});

如果您想要一些视觉效果来向用户表示隐藏,我建议使用 .fadeToggle(),它可以直接起作用:
$('#btnToggle').click(function() {
    $('td.InActive').closest('tr').fadeToggle(1000);
});

Fiddle


额外说明:如果您可以修改生成表格的代码(例如 PHP 或 Ruby 代码),请尝试将 active / inactive 类放在行上(即 <tr> 节点)而不是内部单元格(即 <td> 节点)上:

<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>

这将使您的 CSS 和 JS 规则更易于编写:
//css
tr.inactive {font-style: italic; background-color: #eee}

//js
$('tr.inactive').fadeToggle(1000);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接