h3tr1ck
h3tr1ck

Reputation: 909

HTML table sort

So basically I am running a mysql query that fetches data from my database and displays it in an easy to read layout for my users.

Name-----Address----Sales Person

You get the gist. And now I want to let the user sort the html table by let's say sales person. How would I easily do that using a drop down menu?

<div class='menu'>
  <ul>
    <li><a href='#'><span>Sales Person</span></a>
      <ul>
        <li><a href='#'><span>Melissa</span></a></li>
        <li><a href='#'><span>Justin</span></a></li>
        <li><a href='#'><span>Judy</span></a></li>
        <li><a href='#'><span>Skipper</span></a></li>
        <li><a href='#'><span>Alex</span></a></li>
      </ul>
    </li>
  </ul>
</div>

Upvotes: 54

Views: 280720

Answers (8)

smilyface
smilyface

Reputation: 5513

Here is another library: sorttable.js.

Changes required are -

  1. Add sorttable js

  2. Add class name sortable to table.

Click the table headers to sort the table accordingly:

<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>

<table class="sortable">
  <tr>
    <th>Name</th>
    <th>Address</th>
    <th>Sales Person</th>
  </tr>

  <tr class="item">
    <td>user:0001</td>
    <td>UK</td>
    <td>Melissa</td>
  </tr>
  <tr class="item">
    <td>user:0002</td>
    <td>France</td>
    <td>Justin</td>
  </tr>
  <tr class="item">
    <td>user:0003</td>
    <td>San Francisco</td>
    <td>Judy</td>
  </tr>
  <tr class="item">
    <td>user:0004</td>
    <td>Canada</td>
    <td>Skipper</td>
  </tr>
  <tr class="item">
    <td>user:0005</td>
    <td>Christchurch</td>
    <td>Alex</td>
  </tr>

</table>

Upvotes: 64

Tom P
Tom P

Reputation: 5659

The way I have sorted HTML tables in the browser uses plain, unadorned Javascript.

The basic process is:

  1. add a click handler to each table header
  2. the click handler notes the index of the column to be sorted
  3. the table is converted to an array of arrays (rows and cells)
  4. that array is sorted using javascript sort function
  5. the data from the sorted array is inserted back into the HTML table

The table should, of course, be nice HTML. Something like this...

<table>
 <thead>
  <tr><th>Name</th><th>Age</th></tr>
 </thead>
 <tbody>
  <tr><td>Sioned</td><td>62</td></tr>
  <tr><td>Dylan</td><td>37</td></tr>
  ...etc...
 </tbody>
</table>

So, first adding the click handlers...

const table = document.querySelector('table'); //get the table to be sorted

table.querySelectorAll('th') // get all the table header elements
  .forEach((element, columnNo)=>{ // add a click handler for each 
    element.addEventListener('click', event => {
        sortTable(table, columnNo); //call a function which sorts the table by a given column number
    })
  })

This won't work right now because the sortTable function which is called in the event handler doesn't exist.

Lets write it...

function sortTable(table, sortColumn){
  // get the data from the table cells
  const tableBody = table.querySelector('tbody')
  const tableData = table2data(tableBody);
  // sort the extracted data
  tableData.sort((a, b)=>{
    if(a[sortColumn] > b[sortColumn]){
      return 1;
    }
    return -1;
  })
  // put the sorted data back into the table
  data2table(tableBody, tableData);
}

So now we get to the meat of the problem, we need to make the functions table2data to get data out of the table, and data2table to put it back in once sorted.

Here they are ...

// this function gets data from the rows and cells 
// within an html tbody element
function table2data(tableBody){
  const tableData = []; // create the array that'll hold the data rows
  tableBody.querySelectorAll('tr')
    .forEach(row=>{  // for each table row...
      const rowData = [];  // make an array for that row
      row.querySelectorAll('td')  // for each cell in that row
        .forEach(cell=>{
          rowData.push(cell.innerText);  // add it to the row data
        })
      tableData.push(rowData);  // add the full row to the table data 
    });
  return tableData;
}

// this function puts data into an html tbody element
function data2table(tableBody, tableData){
  tableBody.querySelectorAll('tr') // for each table row...
    .forEach((row, i)=>{  
      const rowData = tableData[i]; // get the array for the row data
      row.querySelectorAll('td')  // for each table cell ...
        .forEach((cell, j)=>{
          cell.innerText = rowData[j]; // put the appropriate array element into the cell
        })
    });
}

And that should do it.

A couple of things that you may wish to add (or reasons why you may wish to use an off the shelf solution): An option to change the direction and type of sort i.e. you may wish to sort some columns numerically ("10" > "2" is false because they're strings, probably not what you want). The ability to mark a column as sorted. Some kind of data validation.

Upvotes: 26

Dr.Sai
Dr.Sai

Reputation: 383

Lets us first create an array of objects

var EmployeeArr = [];
for (let i = 0; i < 10; ++i) {
    let emp = {};
    emp.name = "Asmi_" + i;
    emp.age = Math.round((Math.random() * 100 + 10)) * 3
    EmployeeArr.push(emp);
}

Then Let us make a Table -- we need JQuery and Bootstrap

<style>
.sort {
    padding: 2px;
    cursor: pointer;
}
</style>
$.makeTable = function (mydata, cssClass) {
    if (!cssClass) cssClass = "table table-bordered table-stripped table-dark"
    var table = $('<table class="' + cssClass + '">');
    var tblHeader = "<thead><tr>";
    for (var k in mydata[0]) tblHeader += "<th>" + k +
        `<span class='float-end'>
            <span sort class="sort rounded p-2" onclick="$.Sort(event, '`+ k + `', 'asc' )">&uArr;</span>
            <span sort class="sort rounded p-2" onclick="$.Sort(event,'`+ k + `', 'dsc')">&dArr;</span>
        </span>`
        + "</th>";
    tblHeader += "</tr></thead>";
    $(tblHeader).appendTo(table);
    var TableRow = "<tbody>";
    $.each(mydata, function (index, value) {
        TableRow += "<tr id=_ID_>".replace("_ID_", value.name);
        $.each(value, function (key, val) {
            TableRow += "<td>" + val + "</td>";
        });
        TableRow += "</tr>";
    });
    TableRow += "</tbody>";
    $(table).append(TableRow);
    return ($(table));
};

Let us use the above jQuery Extension MakeTable

var table = $.makeTable(EmployeeArr );
$("#dynArray").empty();
$(table).appendTo("#dynArray");

Let me further extend jQuery to Sort the Table

$.Sort = function (e, colName, sortType) {
    let jSymbol = $(e.target);
    let jTable = $(e.target).closest("table");
    if (jTable.find("tr[ribal]").length === 0) {
        let jTr = $(e.target).closest("tr");
        let rowCount = jTr.children().length;
        let strDispHTML = "<tr ribal ><th status colspan='" + rowCount + "'>Welcome to Sort a Table</th></tr>";
        let tHead = jTable.find("thead")
        tHead.prepend(strDispHTML);
    }
    let status = "Sorting <span class='text-warning'>" + colName + "</span> in <span class='text-warning'>ascending</span> order";
    if (sortType === "dsc") status = status.replace("ascending", "descending");
    jTable.find("th[status]").html(status);

    let jParentTH = jSymbol.closest("th");
    let jParentTR = jParentTH.closest("tr");
    jParentTR.find("span[sort]").removeClass("bg-primary")
    jSymbol.addClass("bg-primary")

    let index = jParentTH.index();
    let tbd = jTable.find("tbody");
    let trs = tbd.children("tr").toArray();
    let arrayOfColumnTextVSTRElement = [];
    trs.forEach(tr => {
        let keyVal = {};
        let jtr = $(tr);
        keyVal.key = $(jtr.children("td")[index]).text(); //could be troublesome
        keyVal.val = jtr;
        arrayOfColumnTextVSTRElement.push(keyVal);
    });
    arrayOfColumnTextVSTRElement.sort((kv1, kv2) => {
        let lhs = parseInt(kv1.key);
        if (Number.isNaN(lhs)) {
            if (sortType == "asc") return kv2.key.localeCompare(kv1.key);
            return kv1.key.localeCompare(kv2.key);
        }
        let rhs = parseInt(kv2.key);
        if (sortType == "asc") return rhs - lhs;
        return lhs - rhs;
    })
    arrayOfColumnTextVSTRElement.forEach(kvp => tbd.prepend(kvp.val));
}

Thats all copy paste the above in a HTML Page and you have sorting.

Upvotes: 0

Douglas Vicentini
Douglas Vicentini

Reputation: 323

Multiple tables, different data types and no external libraries

https://github.com/VFDouglas/HTML-Order-Table-By-Column/blob/main/index.html

Steps:

  1. Add event listeners to all table headers.
  2. Find the table related to the clicked header and get the order icon.
  3. Declare an object to store the table rows(tr) and an array of values of the selected column.
  4. Iterate the values and check the data type for future sorting.
  5. Sort values and change the table header(th) icon.
  6. Replace the old tbody with the ordered html.

Edit (Jan 11 2023): Adding date sort support.

Just add an attribute called data-timestamp and pass the date timestamp to it. The code will take care of the rest.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<table>
    <thead>
    <tr>
        <th>Column 1 <span>&uarr;</span></th>
        <th>Column 2 <span>&uarr;</span></th>
        <th>Column 3 <span>&uarr;</span></th>
        <th>Column 4 <span>&uarr;</span></th>
        <th>Column 5</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>100</td>
        <td>Nome do produto 22</td>
        <td>ABCASD</td>
        <td>22DDS</td>
        <td>454645</td>
    </tr>
    <tr>
        <td>99</td>
        <td>Nome do produto 12</td>
        <td>AACASD</td>
        <td>22DDS</td>
        <td>354645</td>
    </tr>
    <tr>
        <td>300</td>
        <td>Nome do produto 22</td>
        <td>AcCASD</td>
        <td>32DDS</td>
        <td>554649</td>
    </tr>
    <tr>
        <td>400</td>
        <td>Nomde do produto 22</td>
        <td>AcdCASD</td>
        <td>3d2DDS</td>
        <td>554645</td>
    </tr>
    <tr>
        <td>10</td>
        <td>Nome do produto 1</td>
        <td>cCASD</td>
        <td>DDS</td>
        <td>4645</td>
    </tr>
    </tbody>
</table>
<br>
<table>
    <thead>
    <tr>
        <th>Column 1 <span>&uarr;</span></th>
        <th>Column 2 <span>&uarr;</span></th>
        <th>Column 3 <span>&uarr;</span></th>
        <th>Column 4 <span>&uarr;</span></th>
        <th>Column 5</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>100</td>
        <td>Nome do produto 22</td>
        <td>ABCASD</td>
        <td>22DDS</td>
        <td>454645</td>
    </tr>
    <tr>
        <td>99</td>
        <td>Nome do produto 12</td>
        <td>AACASD</td>
        <td>22DDS</td>
        <td>354645</td>
    </tr>
    <tr>
        <td>300</td>
        <td>Nome do produto 22</td>
        <td>AcCASD</td>
        <td>32DDS</td>
        <td>554649</td>
    </tr>
    <tr>
        <td>400</td>
        <td>Nomde do produto 22</td>
        <td>AcdCASD</td>
        <td>3d2DDS</td>
        <td>554645</td>
    </tr>
    <tr>
        <td>10</td>
        <td>Nome do produto 1</td>
        <td>cCASD</td>
        <td>DDS</td>
        <td>4645</td>
    </tr>
    </tbody>
</table>
<br>
<table>
    <thead>
    <tr>
        <th>Column 12222222222 <span>&uarr;</span></th>
        <th>Column 2 <span>&uarr;</span></th>
        <th>Column 3 <span>&uarr;</span></th>
        <th>Column 4 <span>&uarr;</span></th>
        <th>Column 5 <span>&uarr;</span></th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>100</td>
        <td>Nome 221</td>
        <td>ABCASD</td>
        <td>D</td>
        <td data-timestamp="1671667200">22/12/2022</td>
    </tr>
    <tr>
        <td>99</td>
        <td>Nome 12</td>
        <td>AACASD</td>
        <td>C</td>
        <td data-timestamp="1671840000">24/12/2022</td>
    </tr>
    <tr>
        <td>300</td>
        <td>Nome 222</td>
        <td>AcCASD</td>
        <td>A</td>
        <td data-timestamp="1671494400">20/12/2022</td>
    </tr>
    <tr>
        <td>400</td>
        <td>Nome 22</td>
        <td>AcdCASD</td>
        <td>B</td>
        <td data-timestamp="1702857600">18/12/2023</td>
    </tr>
    <tr>
        <td>10</td>
        <td>Nome 11</td>
        <td>cCASD</td>
        <td>A</td>
        <td data-timestamp="1672012800">26/12/2022</td>
    </tr>
    </tbody>
</table>
<script>
    window.onload = function() {
        document.querySelectorAll('th').forEach((element) => { // Table headers
            element.addEventListener('click', function() {
                let table = this.closest('table');

                // If the column is sortable
                if (this.querySelector('span')) {
                    let order_icon = this.querySelector('span');
                    let order      = encodeURI(order_icon.innerHTML).includes('%E2%86%91') ? 'desc' : 'asc';
                    let separator  = '-----'; // Separate the value of it's index, so data keeps intact

                    let value_list = {}; // <tr> Object
                    let obj_key    = []; // Values of selected column

                    let string_count = 0;
                    let number_count = 0;

                    // <tbody> rows
                    table.querySelectorAll('tbody tr').forEach((line, index_line) => {
                        // Value of each field
                        let key = line.children[element.cellIndex].textContent.toUpperCase();

                        // Check if value is date, numeric or string
                        if (line.children[element.cellIndex].hasAttribute('data-timestamp')) {
                            // if value is date, we store it's timestamp, so we can sort like a number
                            key = line.children[element.cellIndex].getAttribute('data-timestamp');
                        }
                        else if (key.replace('-', '').match(/^[0-9,.]*$/g)) {
                            number_count++;
                        }
                        else {
                            string_count++;
                        }

                        value_list[key + separator + index_line] = line.outerHTML.replace(/(\t)|(\n)/g, ''); // Adding <tr> to object
                        obj_key.push(key + separator + index_line);
                    });
                    if (string_count === 0) { // If all values are numeric
                        obj_key.sort(function(a, b) {
                            return a.split(separator)[0] - b.split(separator)[0];
                        });
                    }
                    else {
                        obj_key.sort();
                    }

                    if (order === 'desc') {
                        obj_key.reverse();
                        order_icon.innerHTML = '&darr;';
                    }
                    else {
                        order_icon.innerHTML = '&uarr;';
                    }

                    let html = '';
                    obj_key.forEach(function(chave) {
                        html += value_list[chave];
                    });
                    table.getElementsByTagName('tbody')[0].innerHTML = html;
                }
            });
        });
    }
</script>
</body>
</html>

Upvotes: 9

Joe Webb
Joe Webb

Reputation: 71

A more simplistic approach that I came across for a simple HTML table is to include this snippit when defining your table th tags for each of your columns where #datatable-basic is the id of your table tag where the nth-child represents your columns:

 onclick="w3.sortHTML('#datatable-basic', '.item', 'td:nth-child(1)')

So a full th tag would look like this:

<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7"
                      onclick="w3.sortHTML('#datatable-basic', '.item', 'td:nth-child(4)')" >Date</th>

Then each of your row tags needs an item class like this if you are using flask like I was

<tbody>
<tr class="item" > row text here</tr>
</tbody>

Then you will need to add in this script in your template:

<script src="https://www.w3schools.com/lib/w3.js"></script>

A full working example in a codepen can be found here: https://codepen.io/acity7/pen/QWmqgQR

This works by building off of code from https://www.w3schools.com/w3js/w3js_sort.asp for a very easy implementation to sort a table without using a database or fancier JS packages like Fancytable.js that can be sorted by columns dynamically.

Bonus feature - here is how you can add in a dynamic sort simply.

In your table body tag, add an id like id="table":

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

Add these scripts to your header:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
 <script>
  $("#search").on("keyup", function() {
  var value = $(this).val().toLowerCase();
  $("#table tr").filter(function() {
    $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
  });
});
  </script>

And add a search bar with the id="search" to search through the tr tags and filter them directly in your table:

<input id="search" type="text" class="form-control"  placeholder="Search for name and email......">

Upvotes: 1

Ivan Ivanov
Ivan Ivanov

Reputation: 146

Flexbox-based tables can easily be sorted by using flexbox property "order".

Here's an example:

function sortTable() {
  let table = document.querySelector("#table")
  let children = [...table.children]
  let sortedArr = children.map(e => e.innerText).sort((a, b) => a.localeCompare(b));

  children.forEach(child => {
    child.style.order = sortedArr.indexOf(child.innerText)
  })
}

document.querySelector("#sort").addEventListener("click", sortTable)
#table {
  display: flex;
  flex-direction: column
}
<div id="table">
  <div>Melissa</div>
  <div>Justin</div>
  <div>Judy</div>
  <div>Skipper</div>
  <div>Alex</div>
</div>
<button id="sort"> sort </button>

Explanation

The sortTable function extracts the data of the table into an array, which is then sorted in alphabetic order. After that we loop through the table items and assign the CSS property order equal to index of an item's data in our sorted array.

Upvotes: -2

Penny Liu
Penny Liu

Reputation: 17388

Another approach to sort HTML table. (based on W3.JS HTML Sort)

let tid = "#usersTable";
let headers = document.querySelectorAll(tid + " th");

// Sort the table element when clicking on the table headers
headers.forEach(function(element, i) {
  element.addEventListener("click", function() {
    w3.sortHTML(tid, ".item", "td:nth-child(" + (i + 1) + ")");
  });
});
th {
  cursor: pointer;
  background-color: coral;
}
<script src="https://www.w3schools.com/lib/w3.js"></script>
<link href="https://www.w3schools.com/w3css/4/w3.css" rel="stylesheet" />
<p>Click the <strong>table headers</strong> to sort the table accordingly:</p>

<table id="usersTable" class="w3-table-all">
  <!--     
  <tr>
    <th onclick="w3.sortHTML('#usersTable', '.item', 'td:nth-child(1)')">Name</th>
    <th onclick="w3.sortHTML('#usersTable', '.item', 'td:nth-child(2)')">Address</th>
    <th onclick="w3.sortHTML('#usersTable', '.item', 'td:nth-child(3)')">Sales Person</th>
  </tr> 
  -->
  <tr>
    <th>Name</th>
    <th>Address</th>
    <th>Sales Person</th>
  </tr>

  <tr class="item">
    <td>user:2911002</td>
    <td>UK</td>
    <td>Melissa</td>
  </tr>
  <tr class="item">
    <td>user:2201002</td>
    <td>France</td>
    <td>Justin</td>
  </tr>
  <tr class="item">
    <td>user:2901092</td>
    <td>San Francisco</td>
    <td>Judy</td>
  </tr>
  <tr class="item">
    <td>user:2801002</td>
    <td>Canada</td>
    <td>Skipper</td>
  </tr>
  <tr class="item">
    <td>user:2901009</td>
    <td>Christchurch</td>
    <td>Alex</td>
  </tr>

</table>

Upvotes: 14

verisimilitude
verisimilitude

Reputation: 5110

Check if you could go with any of the below mentioned JQuery plugins. Simply awesome and provide wide range of options to work through, and less pains to integrate. :)

https://github.com/paulopmx/Flexigrid - Flexgrid
http://datatables.net/index - Data tables.
https://github.com/tonytomov/jqGrid

If not, you need to have a link to those table headers that calls a server-side script to invoke the sort.

Upvotes: 56

Related Questions