The previous pages, Sorting using row replacement and Sorting using table replacement demonstrate the goal. The method used to write the data into the document has the major impact on performance. Sorting the data represents a very small part of the processing time. To accomplish our goal there are four pieces that need to be dealt with.

The primary focus of this tutorial is sorting. Coding the hook and loading the data are presented in the section on Loading Tables Dynamically. See the Remote Scripting, XML-RPC, and AJAX section for methods of downloading the source data. There is no reason to duplicate the information on those pages.

These examples use an array coded into the HTML. This is a reasonable approach since the page could be generated by server side code in response to a CGI database request.

The table is not actually sorted, but rather an Array representing the table is sorted and the table is reloaded. The methods to load a table are explained on the pages Load Table by Replacing the Rows and Load Table by Replacing the Table. The data needs to be in an Array to be sorted. The Array object has a sort method that is used.

The data must be in an Array because the Array sort method will be used to sort the data.

The steps are sort the array then load the table. Extracting the data from the table would be a performance hit so it needs to be in the array when the page loads or put into an array when downloaded with AJAX.

Function doSort: the Event Handler  

function doSort(e) {
    e = e || window.event;
    var target = e.srcElement || e.target;
    var ord = "asc"; 
    var column = target.id;
    var className = target.className;

    if (target.nodeName.toLowerCase() == "th") {
        if (className) {
            reverse();
            ord = (className == "asc"?"dsc":"asc");
        } else {
            switch (column) {
            case "fname":
                dataArray.sort(sortByFNameAsc);
                break;
            case "lname":
                dataArray.sort(sortByLNameAsc);
                break;
            case "dept":
                dataArray.sort(sortByDeptAsc);
                break;
            case "office":
                dataArray.sort(sortByOfficeAsc);
                break;
            }
        }
        loadTable2("tableDiv", dataArray, column,  ord);
    }
}

The function doSort is bound to the onclick event of the table or thead when updating by replacing the rows and to the onclick event of table container when updating by replacing the table.

The click event bubbles up to doSort, which determines the sort algorithm based on the ID of the column clicked. If the column has a class name, it is already sorted and all that is necessary is to reverse the array's order and change the heading's class name so an up or down indicator will display.

Loading by Replacing Rows

The function loadTable (not shown, but explained in the section Dynamically Load Table and visible if you view the source of the first page) handles loading the sorted array by replacing the rows. Sorting requires extra arguments, column and ord, representing the column of the primary sort key and the order: ascending or descending. This method is common; albeit, slower than replacing the complete table.

Loading by Replacing the Complete Table

loadTable2 handles loading the sorted array into the table. This particular snippet of code (not shown, but explained in the section Dynamically Load Table and visible if you view the source of the second page) replaces the table. Sorting requires extra arguments, column and ord, representing the column of the primary sort key and the order: ascending or descending.

Sorting Methods  

There are four sorting functions one for each column that sort in ascending order. The first click on a column sorts it in ascending order and subsequent clicks toggle between ascending and descending order by simply reversing the array with the Array object's reverse method.

The sort function is passed as the argument to the Array's sort method: array.sort(func);. Two array elements, in this case arrays (because its a 2 dimenstional array) representing rows are passed to the sort function. If the first row should come before the second, -1 is returned, and, if the second should come before the first, 1 is returned, and, if they are the same, 0 is returned.

Since the requirements call for sublevel sorting (sorting by first and last name within the primary sort), the sort functions test for inequality at each sort level starting with the primary key and then the secondary key, etc. Array element 1 is the fname and element 2 is the lname.

function sortByLNameAsc(a, b){
    if (a[2].toLowerCase() != b[2].toLowerCase())
        return (a[2].toLowerCase() < b[2].toLowerCase() ? -1 : 1);
    else if (a[1].toLowerCase() != b[1].toLowerCase())
        return (a[1].toLowerCase() < b[1].toLowerCase() ? -1 : 1);
    else
        return 0;
}
function sortByFNameAsc(a, b){
    if (a[1].toLowerCase() != b[1].toLowerCase())
        return (a[1].toLowerCase() < b[1].toLowerCase() ? -1 : 1);
    else if (a[2].toLowerCase() != b[2].toLowerCase())
        return (a[2].toLowerCase() < b[2].toLowerCase() ? -1 : 1);
    else
        return 0;    
}
function sortByDeptAsc(a, b){
    if (a[3] != b[3]) 
        return (a[3] < b[3] ? -1 : 1);
    else if (a[2].toLowerCase() != b[2].toLowerCase())
        return (a[2].toLowerCase() < b[2].toLowerCase() ? -1 : 1);
    else if (a[1].toLowerCase() != b[1].toLowerCase())
        return (a[1].toLowerCase() < b[1].toLowerCase() ? -1 : 1);
    else
        return 0;
}
function sortByOfficeAsc(a, b){
    if (a[4] != b[4])
        return (a[4] * 1 < b[4] * 1 ? -1 : 1);
    else if (a[2].toLowerCase() != b[2].toLowerCase())
        return (a[2].toLowerCase() < b[2].toLowerCase() ? -1 : 1);
    else if (a[1].toLowerCase() != b[1].toLowerCase())
        return (a[1].toLowerCase() < b[1].toLowerCase() ? -1 : 1);
    else
        return 0;    
}
function reverse() {
    dataArray.reverse();
}

That's the basics of sorting tables.