Change order of table rows

I use PHP to create an HTML table with information from a database, ordered by date. Some of the rows of the table have a class named “new”, while the others don’t have any class. What I would like to do is to have all the rows with class “new” shown as last rows of that specific date.

So for example:


<tr>...<td>01-01-2011</td>...</tr>
<tr>...<td>01-01-2011</td>...</tr>
<tr class="new">...<td>01-01-2011</td>...</tr>
<tr class="new">...<td>01-01-2011</td>...</tr>

Is this possible using javascript/jquery?

One solution would be to tag the <tr>'s with an id attribute and use jquery’s sortable behavior.

Normally a sort function looks like this:


function (a, b) {
    if (a < b) {
        return -1;
    }
    if (a > b) {
        return 1;
    }
    return 0;
}

So, we can take that and modify it to work the way you want.

First we’ll need to get the table rows in to an array, so that w can then use the array sort features.
Then with the sort function, a and b are going to be table rows, so if they’re not table rows we should just exit the function. We get the date from the table data, and sort based on that.
If the dates are the same we compare the class name instead. If one of the classes is “new” and the other isn’t, we can immediately return that new one as the preferred sort.

Let’s try that out.

I’m going to get the innerHTML of the first TD element, so depending on your table structure, you may need to use some other technique to retrieve the date.


function compareTRDates(a, b) {
    aDate = Date.parse(a.getElementsByTagName('td')[0].innerHTML);
    bDate = Date.parse(b.getElementsByTagName('td')[0].innerHTML);
    if (aDate < bDate) {
        return -1;
    }
    if (aDate > bDate) {
        return 1;
    }
    return 0;
}

Comparing the new class name is straight-forward by comparison:


function compareNewClass(a, b) {
    if (a.className !== 'new' && b.className === 'new') {
        return -1;
    }
    if (a.className === 'new' && b.className !== 'new') {
        return 1;
    }
    return 0;
}

Lastly, you’ll want to compare the dates, and if the dates are equal, to compare by the new class name instead.


function compareTRDatesThenNew(a, b) {
    return compareTRDates(a, b) || compareNewClass(a, b);
}

Here’s a simple test page that lets you test the sorting.


<html>
<style type="text/css">
.new {
    background: lightgreen;
}
</style>
</head>
<body>
<table id="databaseInfo">
    <tr class="new">...<td>01-01-2011</td>...</tr>
    <tr>...<td>10-01-2011</td>...</tr>
    <tr>...<td>01-01-2011</td>...</tr>
    <tr>...<td>01-10-2010</td>...</tr>
    <tr>...<td>01-01-2011</td>...</tr>
    <tr class="new">...<td>01-01-2011</td>...</tr>
</table>
<button id="sortbydate">Sort by Date</button>
<button id="sortbynew">Sort by New</button>
<button id="sortbydatethennew">Sort by Date then New</button>
<script>
function compareTRDates(a, b) {
    aDate = Date.parse(a.getElementsByTagName('td')[0].innerHTML);
    bDate = Date.parse(b.getElementsByTagName('td')[0].innerHTML);
    if (aDate < bDate) {
        return -1;
    }
    if (aDate > bDate) {
        return 1;
    }
    return 0;
}
function compareNewClass(a, b) {
    if (a.className !== 'new' && b.className === 'new') {
        return -1;
    }
    if (a.className === 'new' && b.className !== 'new') {
        return 1;
    }
    return 0;
}
function compareTRDatesThenNew(a, b) {
    return compareTRDates(a, b) || compareNewClass(a, b);
}
function sortTableRows(compareFunction) {
    var table = document.getElementById('databaseInfo'),
        rows = table.getElementsByTagName('tr');
    rows = Array.prototype.splice.call(rows, 0);
    rows.sort(compareFunction);
    
    while (table.hasChildNodes()) {
        table.removeChild(table.firstChild);
    }
    for (i = 0; i < rows.length; i += 1) {
        table.appendChild(rows[i]);
    }
}

document.getElementById('sortbydate').onclick = function () {
    sortTableRows(compareTRDates);
};
document.getElementById('sortbynew').onclick = function () {
    sortTableRows(compareNewClass);
};
document.getElementById('sortbydatethennew').onclick = function () {
    sortTableRows(compareTRDatesThenNew);
};
</script>
</body>
</html>

Isn’t that used for dragging and dropping elements?

Do you have any idea why Firebug gives me a “getElementsByTagName is not defined” error?

I’ve just tried it in Firefox and found that Firefox has trouble parsing dates in a dd-mm-yyyy or mm-dd-yyyy format, so we’ll have to interpret those dates ourself manually.

You don’t mention whether the dates you are using are dd-mm-yyyy or mm-dd-yyyy so I will presume that you are in the majority who use dd-mm-yyyy

Because some web browsers aren’t as good at parsing dates, we have to break up those dates in to their component parts and give them to the parse function in a way that the web browser can understand.


// take dates from dd-mm-yyyy format and parse as yyyy-mm-dd
var aText = a.getElementsByTagName('td')[0].innerHTML,
    bText = b.getElementsByTagName('td')[0].innerHTML,
    aMatch = aText.match(/(\\d*)-(\\d*)-(\\d*)/),
    bMatch = bText.match(/(\\d*)-(\\d*)-(\\d*)/),
    aDate = Date.parse(aMatch[3] + '-' + aMatch[2] + '-' + aMatch[1]),
    bDate = Date.parse(bMatch[3] + '-' + bMatch[2] + '-' + bMatch[1]);

Also with Internet Explorer, there can be some troubles with updating the table, so we’re going to work with the tbody element itself to keep IE happier.


<table id="databaseInfo">
    <tbody>
        ...
    </tbody>
</table>

The following sample code should now work more consistently across multiple web browsers.


<html>
<style type="text/css">
.new {
    background: lightgreen;
}
</style>
</head>
<body>
<table id="databaseInfo">
    <tbody>
        <tr class="new"><td>01-01-2011</td></tr>
        <tr><td>10-01-2011</td></tr>
        <tr><td>01-01-2011</td></tr>
        <tr><td>01-10-2011</td></tr>
        <tr><td>01-01-2011</td></tr>
        <tr class="new"><td>01-01-2011</td></tr>
    </tbody>
</table>
<button id="sortbydate">Sort by Date</button>
<button id="sortbynew">Sort by New</button>
<button id="sortbydatethennew">Sort by Date then New</button>
<script>
Array.fromSequence = function (seq) {
    var arr = [],
        i;
    for (i = 0; i < seq.length; i += 1) {
        if (seq[i]) {
            arr[i] = seq[i];
        }
    }
    return arr;
};
function compareTRDates(a, b) {
    // take dates from dd-mm-yyyy format and parse as yyyy-mm-dd
    var aText = a.getElementsByTagName('td')[0].innerHTML,
        bText = b.getElementsByTagName('td')[0].innerHTML,
        aMatch = aText.match(/(\\d*)-(\\d*)-(\\d*)/),
        bMatch = bText.match(/(\\d*)-(\\d*)-(\\d*)/),
        aDate = Date.parse(aMatch[3] + '-' + aMatch[2] + '-' + aMatch[1]),
        bDate = Date.parse(bMatch[3] + '-' + bMatch[2] + '-' + bMatch[1]);
    if (aDate < bDate) {
        return -1;
    }
    if (aDate > bDate) {
        return 1;
    }
    return 0;
}
function compareNewClass(a, b) {
    if (a.className !== 'new' && b.className === 'new') {
        return -1;
    }
    if (a.className === 'new' && b.className !== 'new') {
        return 1;
    }
    return 0;
}
function compareTRDatesThenNew(a, b) {
    return compareTRDates(a, b) || compareNewClass(a, b);
}
function sortTableRows(compareFunction) {
    var table = document.getElementById('databaseInfo'),
        tbody = table.getElementsByTagName('tbody')[0],
        rows = tbody.getElementsByTagName('tr'),
        i;
    rows = Array.fromSequence(rows);
    rows.sort(compareFunction);
    while (tbody.hasChildNodes()) {
        tbody.removeChild(tbody.firstChild);
    }
    for (i = 0; i < rows.length; i += 1) {
        tbody.appendChild(rows[i]);
    }
}
 
document.getElementById('sortbydate').onclick = function () {
    sortTableRows(compareTRDates);
};
document.getElementById('sortbynew').onclick = function () {
    sortTableRows(compareNewClass);
};
document.getElementById('sortbydatethennew').onclick = function () {
    sortTableRows(compareTRDatesThenNew);
};
</script>
</body>
</html>

Now it works, thanks a lot :slight_smile:

I just have one question though: why do you use

return -1

and

return 1

to sort the values? I thought that you had to use arrays to separate values :confused:

That’s how the sort function determines which order the values are in. -1 means that the a value is smaller than the b value. 1 means that the a value is greater than the b value.

If you were sorting numbers, you could return a-b instead.

See the sort documentation for the full details about that.

Now I understand, thank you :slight_smile: