HopAlongPolly
HopAlongPolly

Reputation: 1433

Sorting multiple html tables with multiple sorts

I have several html tables on my page and I want to be able to sort them based on the value of one of their cell values. Here is an example of my tables

<div id="tables">
    <table class="sortable">
        <tr>
            <td>Comic Book Name</td>
            <td id="comic">Batman</td>
        </tr>
        <tr>
            <td>Main Character</td >
            <td ="character">Bruce Wayne</td>
        </tr>
        <tr>
            <td>Hero Name</td>
            <td id="hero">Batman</td>
        </tr>
        <tr>
            <td>Published</td>
            <td id="publish">05/01/1940</td>
        </tr>
    </table>
    <table class="sortable">
        <tr>
            <td>Comic Book Name</td>
            <td id="comic">Green Arrow</td>
        </tr>
        <tr>
            <td>Main Character</td >
            <td ="character">Oliver Queen</td>
        </tr>
        <tr>
            <td>Hero Name</td>
            <td id="hero">Green Arrow</td>
        </tr>
        <tr>
            <td>Published</td>
            <td id="publish">11/01/1941</td>
        </tr>
    </table>
</div>

I am providing the following drop down to allow users to filter.

<select id="OrderBy">
 <option selected="selected" value="comic">Comic Name</option>
 <option value="character">Character Name</option>
 <option value="hero">Hero Name</option>
 <option value="publish">Earliest Publish Date</option>
</select>

My hopes here are to sort the tables alphabetically by Comic book name when they choose Comic Name, alphabetically by Character Name when they select character name etc. etc.

Here is the javascript/jquery I have come up with so far. I'm sure this could be done better but I'm new to Javascript and Jquery.

$('#OrderBy').on('change', function () {
    _order = $('#OrderBy').val();
    switch (_order) {
        case "comic":
            $('#sortable').sort(function () {

            });
            break;
        case "character":
            $('#sortable').children().sort(function (a, b) {
            });
            break;
        case "publish":
            $('#sortable').sort(function () {
            });
            break;
        case "publish":
            $('#sortable').sort(function () {
                if ($(b).children().find('#Hours').text() - $(a).children().find('#publish').text() <= 0) {
                    return $(a) - $(b);
                }
                return $(b) - $(a);
            });
            break;
    }
});

Everywhere I look someone suggests Sorttables or tableSorter but I've only seen how they would work for a single table whose rows are being sorted. I know what everyone is thinking at this point, don't use tables this way. My problem is there is already a bunch of jquery and javascript supporting the tables and I'm not experienced enough in the languages to re-write all of that.

I like the idea of pushing everything onto a stack as described here but I always generated an error about how my array/stack was not defined.

Upvotes: 5

Views: 2502

Answers (2)

n00dl3
n00dl3

Reputation: 21564

First, you should not put several times the same id, but use classes instead:

<div id="tables">

    <table class="sortable">
        <tr>
            <td>Comic Book Name</td>
            <td class="comic">Green Arrow</td>
        </tr>
        <tr>
            <td>Main Character</td >
            <td class="character">Oliver Queen</td>
        </tr>
        <tr>
            <td>Hero Name</td>
            <td class="hero">Green Arrow</td>
        </tr>
        <tr>
            <td>Published</td>
            <td class="publish">11/01/1941</td>
        </tr>
    </table>
    <table class="sortable">
        <tr>
            <td>Comic Book Name</td>
            <td class="comic">Batman</td>
        </tr>
        <tr>
            <td>Main Character</td >
            <td class="character">Bruce Wayne</td>
        </tr>
        <tr>
            <td>Hero Name</td>
            <td class="hero">Batman</td>
        </tr>
        <tr>
            <td>Published</td>
            <td class="publish">05/01/1940</td>
        </tr>
    </table>
</div>

For your problem, I would use the native Array.prototype.sort method

var tables = [];
var $tables = $("table.sortable");
var container = $("#tables");
// this is the only specific case, as the date does not start by the year
// it will be used to sort by publish date
function sortByDate(a, b) {
    return new Date(a.publish).getTime() - new Date(b.publish).getTime();
}

function sortTables(order) {
    var sort;
    if (order === "publish") {
        tables = tables.sort(sortByDate);
    } else {
        tables = tables.sort(function(a, b) {
            return a[order] < b[order] ? -1 : 1;
        });
    }
    tables.forEach(function(data,i) {
        tables[i].$el.detach()
        container.append(tables[i].el);
    });
}

function init() {
    //populate the tables array that will be sorted
    $tables.each(function(i, val) {
        var $this = $(this);
        tables.push({
            $el: $this,
            el: this,
            comic: $this.find(".comic").text(),
            character: $this.find(".character").text(),
            hero: $this.find(".hero").text(),
            publish: $this.find(".publish").text()
        });
    });
    $("#OrderBy").on("change", function(event) {
        sortTables(event.currentTarget.value);
    });
    //by default sort by Hero
    sortTables("hero");
}
init();

Upvotes: 1

Sandipan Guha
Sandipan Guha

Reputation: 126

Primarily, Sort can't happen when you have data in two different tables. To make it possible, you need to put id to them, not just classes because your primary issue isn't CSS. Again, if you put same id to different contents, it can't be spotted specifically. So, debugged code would be:

<div id="tables">
<table id="tbl1" class="sortable">
    <tr>
        <td>Comic Book Name</td>
        <td class="comic">Batman</td>
    </tr>
    <tr>
        <td>Main Character</td >
        <td class="character">Bruce Wayne</td>
    </tr>
    <tr>
        <td>Hero Name</td>
        <td class="hero">Batman</td>
    </tr>
    <tr>
        <td>Published</td>
        <td class="publish">05/01/1940</td>
    </tr>
</table>

<table id="tbl2" class="sortable">
    <tr>
        <td>Comic Book Name</td>
        <td class="comic">Green Arrow</td>
    </tr>
    <tr>
        <td>Main Character</td >
        <td class="character">Oliver Queen</td>
    </tr>
    <tr>
        <td>Hero Name</td>
        <td class="hero">Green Arrow</td>
    </tr>
    <tr>
        <td>Published</td>
        <td class="publish">11/01/1941</td>
    </tr>
</table>

Your drop-down menu(filter box) is properly defined. Now, in js code, there are some syntactical mistakes. like, 'class' identifier is '.', 'id' identifier is '#', you used '#' instead of '.' some places :

switch (_order) {
    case "comic":
        $('#sortable').sort(function () {});

debugged code would be:

$('#OrderBy').on('change', function () {
_order = $('#OrderBy').val();
switch (_order) {
    case "comic":
        $('.sortable').sort(function () {});
        break;
    case "character":
        $('.sortable').children().sort(function (a, b) {
        });
        break;
    case "publish":
        $('.sortable').sort(function () {
        });
        break;
    case "publish":
        $('.sortable').sort(function () {
            if ($(b).children().find('#Hours').text() - $(a).children().find('#publish').text() <= 0) {
                return $(a) - $(b);
            }
            return $(b) - $(a);
        });
        break;
}});

There's no official table sort() function jquery library. It would work for array only, but you won't have any array here. So we make a sort function on our own. We can name it 'sortTable()'. The code for this should be:

<!--   -------------------------------------------------------------- -->
<!-- Secondary functions -->

<!-- Creates a new table to display the sorted Table -->

function createEmptyTable(){
    var outerDiv = document.createElement('div');
    outerDiv.setAttribute('id','ResultTable');
    var innerTable = doucment.createElement('table');
    innerTable.setAttribute('id','sortedTable'); 
    var newRows = new Array();
    var recordIndex = document.getElementsByClassName('comic');
    innerTable.innerHTML = "<tr> <th>Comic Book Name </th> <th>Main Character</th> <th>Hero Name</th> <th>Published</th> </tr>";
    for(var i=0; i<recordIndex.Length;i++){
        newRows[i]= document.createElement('tr');
        outerDiv.appendChild(innerTable).appendChild(newRows[i]);
        }
    }

<!--     -------------------------------------------------------------  -->

<!-- function to find out in which table chosen filter belongs -->

function recordSearch(keyWord){
    var sortableTables = document.getElementsByClassName('sortable');
    for( var i=0; i<sortableTables.length;i++){
        if($(sortableTables[i]).find(keyword)){
        return sortableTable[i];
    }
        }

<!--      -------------------------------------------------------------    -->

<!-- Primary function to sort the Table -->

function sortTable(filtered_option){

<!-- first we delete any existing sorted table -->

    if(document.getElementById('ResultTable')){
        var existingSortedTable = document.getElementbyId('ResultTable');
        existingSortedTable.removeChild(document.getElementById('sortedTable');
    }

<!-- now we create new table & put the result -->

    createEmptyTable();
    var comicNames = document.getElementsByClassName('comic');
    var characterNames = document.getElementsByClassName('character');
    var heroNames = document.getElementsByClassName('hero');
    var publishDates = document.getElementsByClassName('publish');

    switch (filtered_option) {

    case 'comic' :
        var sortedComicNames = comicNames.sort();
        for(var i=0;i<sortedComicNames.length;i++){
            var foundRecord = recordSearch(sortedComicNames[i]);
            var allFields = foundRecord.getElementByTagName('tr').getElementsByClassName('td');
            var emptyRows = document.getElementById('sortedTable').getElementsByTagName('tr');
            for(var j=0;j<allFields.length;j++){
                emptyRows[i+1].innerHTML = "<td>" + allFields[j].value + "</td>" ;
        }}
    break;

    case 'character' :
        var sortedCharacterNames= characterNames.sort();
        for(var i=0;i<characterNames.length;i++){
            var foundRecord = recordSearch(sortedCharacterNames[i]);
            var allFields = foundRecord.getElementByTagName('tr').getElementsByClassName('td');
            var emptyRows = document.getElementById('sortedTable').getElementsByTagName('tr');
            for(var j=0;j<allFields.length;j++){
                emptyRows[i+1].innerHTML = "<td>" + allFields[j].value + "</td>" ;
        }}
    break;

    case 'hero' :
        var sortedHeroNames= heroNames.sort();
        for(var i=0;i<heroNames.length;i++){
            var foundRecord = recordSearch(heroNames[i]);
            var allFields = foundRecord.getElementByTagName('tr').getElementsByClassName('td');
            var emptyRows = document.getElementById('sortedTable').getElementsByTagName('tr');
            for(var j=0;j<allFields.length;j++){
                emptyRows[i+1].innerHTML = "<td>" + allFields[j].value + "</td>" ;
        }}
    break;

    case 'publish' :
        var sortedPublishedDates= publishedDates.sort();
        for(var i=0;i<publishedDates.length;i++){
            var foundRecord = recordSearch(publishedDates[i]);
            var allFields = foundRecord.getElementByTagName('tr').getElementsByClassName('td');
            var emptyRows = document.getElementById('sortedTable').getElementsByTagName('tr');
            for(var j=0;j<allFields.length;j++){
                emptyRows[i+1].innerHTML = "<td>" + allFields[j].value + "</td>" ;
        }}
    break;  
    }
}
<!--    --------------------------------------------------------------------    -->

Put them together and Try. Let me know if it works.

Upvotes: 0

Related Questions