Rob Lauer Rob Lauer - 1 month ago 16
Javascript Question

Sorting Table with Hierarchical Data

I have an odd situation where I have an HTML table with hierarchical data and I need to provide the ability to sort the data, but only the "parent" rows, not the children. In fact, the children should stay attached to the parent, so wherever the parent gets sorted, the children should follow along (but they don't have to be sorted internally). For example:

enter image description here

When I click on "Rating", it should sort the bolded ratings and the children (the weeds in this example) should just go along for the ride. Is there a magical jquery plugin that will do this for me? :)

EDIT Here is a really stripped-down version of the rendered HTML. I have a certain amount of control over this, but not much (the markup comes from a remote endpoint...).

<table id="grid">
<thead>
<tr role="row">
<th>Name</th>
<th>Rating</th>
<th>Timing</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Butyrac 200</b></td>
<td><b>8</b></td>
<td>POST</td>
<td>3</td>
</tr>
<tr>
<td>Common chickweed</td>
<td>4</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Common dandelion</td>
<td>4</td>
<td></td>
<td></td>
</tr>
<tr>
<td><b>Chateau</b></td>
<td><b>14</b></td>
<td>PRE</td>
<td>6</td>
</tr>
<tr>
<td>Common chickweed</td>
<td>10</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Common dandelion</td>
<td>4</td>
<td></td>
<td></td>
</tr>
</tbody>



dc5 dc5
Answer

If you have any say in the matter, ask to have a bit more structure/definition added to the markup so that you can rely on data rather than presentation to determine how these rows are related.

As is, it looks like you will need to rely on certain elements of the presentation being present. This can be fairly brittle as anyone coming along later and changing the presentation can break your logic very quickly.

Here I've made the assumption that only the top level rows have a <b> marker - you may need to adjust this depending on your exact requirements.

Working Fiddle

No magic here, just a bit of elbow grease:

var $tbody = $('#grid > tbody');
var topRows  = [];
var children = [];
$tbody.find('tr').each(function (idx, ele) {
    var $ele = $(ele);

    if ($ele.find('td:eq(0) > b').length) {
        // It's a parent

        // Make new parent
        var newParent = { ele: $ele, children: [] };
        topRows.push(newParent);

        // Keep track of the children
        children = newParent.children;
    } else {
        // It's a child
        children.push($ele);
    }
});

// Sort the top level array by the value of the rating td
topRows.sort(function(topA, topB) {
    var valA = +topA.ele.find('td:eq(1) > b').text();
    var valB = +topB.ele.find('td:eq(1) > b').text();

    return valA - valB;
});

// Detach it temporarily to avoid live DOM updates
$tbody.detach().empty();

// Add the newly sorted parents back in along with their children
$.each(topRows, function(topIdx, top) {
    $tbody.append(top.ele);
    $.each(top.children, function(childIdx, child) {
        $tbody.append(child);
    });
});

$('#grid').append($tbody);
Comments