[ Team LiB ] Previous Section Next Section

14.16 Sorting Dynamic Tables

NN 6, IE 5(Win)

14.16.1 Problem

You want users to be able to view a table sorted according to different column values.

14.16.2 Solution

Sorting a table works best when the data for the table is delivered as JavaScript or XML data, using the kinds of table transformations shown in Recipe 14.6 and Recipe 14.7. The table data does not need to be any different from what was demonstrated in those recipes. The difference is in the fixed table column headings and the functions invoked from links surrounding the heading text.

Design your HTML structure such that clickable user interface elements let users control the table's sorting order. Table header text formatted as hyperlinks is most common. Next, define JavaScript array sorting functions for each of your sorting criteria. See the Discussion for an example. Finally, use a script routine to generate the body of the table based on the current sort order of the JavaScript data array (see the Discussion). Each time the user requests a sorting of the data array, the table body is refreshed with the data in the desired order.

14.16.3 Discussion

For an example of what a sortable table framework looks like, the hardwired HTML portion of the table from Recipe 14.7 is shown here, modified with th cell content fixed with clickable links.

<table id="cupFinals">
<thead>
<tr>
    <th><a href="#" title="Sort by Year" 
           onclick="return sortTable(this)">Year</a></th>
    <th><a href="#" title="Sort by Country" 
           onclick="return sortTable(this)">Host Country</a></th>
    <th><a href="#" title="Sort by Winning Team" 
           onclick="return sortTable(this)">Winner</a></th>
    <th><a href="#" title="Sort by Losing Team" 
           onclick="return sortTable(this)">Loser</a></th>
    <th>Score <a href="#" title="Sort by Winning Score" 
                 onclick="return sortTable(this)">Win</a> - <a href="#" 
                 title="Sort by Losing Score" 
                 onclick="return sortTable(this)">Lose</a></th>
</tr>
</thead>
<tbody id="matchData"></tbody>
</table>

All links invoke the same sortTable( ) function, which acts as a central switchboard to individual array sorting routines. A switch statement branches execution based on the text of the link's th element:

// Sorting function dispatcher (invoked by table column links)
function sortTable(link) {
    switch (link.firstChild.nodeValue) {
        case "Year" :
            jsData.sort(sortByYear);
            break;
        case "Host Country" :
            jsData.sort(sortByHost);
            break;
        case "Winner" :
            jsData.sort(sortByWinner);
            break;
        case "Loser" :
            jsData.sort(sortByLoser);
            break;
        case "Win" :
            jsData.sort(sortByWinScore);
            break;
        case "Lose" :
            jsData.sort(sortByLosScore);
            break;
    }
    drawTable("matchData")
    return false
}

Each of the sorting routines sorts the jsData array based on criteria that examine a specific property of each jsData item's object:

// Sorting functions (invoked by sortTable( ))
function sortByYear(a, b) {
    return a.year - b.year;
}
function sortByHost(a, b) {
    a = a.location.toLowerCase( );
    b = b.location.toLowerCase( );
    return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}
function sortByWinScore(a, b) {
    return b.winScore - a.winScore;
}
function sortByLosScore(a, b) {
    return b.losScore - a.losScore;
}
function sortByWinner(a, b) {
    a = a.winner.toLowerCase( );
    b = b.winner.toLowerCase( );
    return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}
function sortByLoser(a, b) {
    a = a.loser.toLowerCase( );
    b = b.loser.toLowerCase( );
    return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}

Back in the sortTable( ) function, once the jsData table is sorted by the desired property, the drawTable( ) function executes. This is slightly modified over Recipe 14.7 to include a call to another function that clears all rows from the tbody element reserved for the dynamic cells. The modified drawTable( ) method is as follows:

// Draw table from 'jsData' array of objects
function drawTable(tbody) {
    var tr, td;
    tbody = document.getElementById(tbody);
    // remove existing rows, if any
    clearTable(tbody);
    // loop through data source
    for (var i = 0; i < jsData.length; i++) {
        tr = tbody.insertRow(tbody.rows.length);
        td = tr.insertCell(tr.cells.length);
        td.setAttribute("align", "center");
        td.innerHTML = jsData[i].year;
        td = tr.insertCell(tr.cells.length);
        td.innerHTML = jsData[i].location;
        td = tr.insertCell(tr.cells.length);
        td.innerHTML = jsData[i].winner;
        td = tr.insertCell(tr.cells.length);
        td.innerHTML = jsData[i].loser;
        td = tr.insertCell(tr.cells.length);
        td.setAttribute("align", "center");
        td.innerHTML = jsData[i].winScore + " - " + jsData[i].losScore;
    }
}

The clearTable( ) function is a simple loop that removes any rows in the table body section:

// Remove existing table rows
function clearTable(tbody) {
    while (tbody.rows.length > 0) {
        tbody.deleteRow(0);
    }
}

Most of the sorting comparison functions in this recipe inspect properties of objects that occupy each array entry. Because each jsData array entry is an object (see the object definition in Recipe 14.7), the comparison functions repeatedly compare specific properties of the object, such as names of teams and score numbers. To assure accurate string comparisons regardless of case, they are converted to a uniform case (lowercase in the example), so that the differing ASCII values of upper- and lowercase values do not play a role in the comparisons.

14.16.4 See Also

Recipe 14.7 for embedding data as JavaScript objects and arrays (which enhance table sorting possibilities); Recipe 3.5 for simple array sorting; Recipe 3.10 for sorting an array of objects.

    [ Team LiB ] Previous Section Next Section