Reputation: 909
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
Reputation: 5513
Here is another library: sorttable.js
.
Changes required are -
Add sorttable js
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
Reputation: 5659
The way I have sorted HTML tables in the browser uses plain, unadorned Javascript.
The basic process is:
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
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' )">⇑</span>
<span sort class="sort rounded p-2" onclick="$.Sort(event,'`+ k + `', 'dsc')">⇓</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
Reputation: 323
https://github.com/VFDouglas/HTML-Order-Table-By-Column/blob/main/index.html
Steps:
tr
) and an array of values of the selected column.th
) icon.tbody
with the ordered html.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>↑</span></th>
<th>Column 2 <span>↑</span></th>
<th>Column 3 <span>↑</span></th>
<th>Column 4 <span>↑</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>↑</span></th>
<th>Column 2 <span>↑</span></th>
<th>Column 3 <span>↑</span></th>
<th>Column 4 <span>↑</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>↑</span></th>
<th>Column 2 <span>↑</span></th>
<th>Column 3 <span>↑</span></th>
<th>Column 4 <span>↑</span></th>
<th>Column 5 <span>↑</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 = '↓';
}
else {
order_icon.innerHTML = '↑';
}
let html = '';
obj_key.forEach(function(chave) {
html += value_list[chave];
});
table.getElementsByTagName('tbody')[0].innerHTML = html;
}
});
});
}
</script>
</body>
</html>
Upvotes: 9
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
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
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
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