I have a table in html, I have set for each td an id that I will need to sort the table with a Jquery code.
Sorting works with the FireFox browser, but with Chrome it does not work ... do you know how to help me?
$(function() {
$(".table-user-th").click(function() {
var o = $(this).hasClass('asc') ? 'desc' : 'asc';
$('.table-user-th').removeClass('asc').removeClass('desc');
$(this).addClass(o);
var colIndex = $(this).prevAll().length;
var tbod = $(this).closest("table").find("tbody");
var rows = tbod.find("tr");
rows.sort(function(a, b) {
var A = $(a).find("td").eq(colIndex).attr('id');;
var B = $(b).find("td").eq(colIndex).attr('id');;
if (!isNaN(A)) A = Number(A);
if (!isNaN(B)) B = Number(B);
return o == 'asc' ? A > B : B > A;
});
$.each(rows, function(index, ele) {
tbod.append(ele);
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border="1">
<thead>
<tr>
<th class="table-user-th">Firstname</th>
<th class="table-user-th">Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td id="Mark">Mark</td>
<td id="Red">Red</td>
</tr>
<tr>
<td id="Nick">Nick</td>
<td id="Sid">Sid</td>
</tr>
<tr>
<td id="Alex">Alex</td>
<td id="Nirv">Nirv</td>
</tr>
</tbody>
</table>
It seems like there is no purpose of using ids here.
Actually the problem was in you sort function. It should return not just true/false but the numeric difference between two values. As usual it is return -1/0/1
So here I wrote comparator func that does just that. And depending on sort type I just multiply it on -1 or 1.
I've also refactored a little bit your code not to use classes or ids. Using jquery you can use data method that stores data on element by key/value.
$(function() {
function cmp(a,b) {return a < b ? 1 : a > b ? -1 : 0}
$(".sortable-table").on('click', 'th', function() {
var th = $(this);
var colIndex = th.data('column');
if(typeof colIndex === 'undefined') {
return;
}
var sortType = th.data('sort') === 'asc' ? 'desc' : 'asc';
th.data('sort', sortType);
var table = $(this).closest("table");
table.find('thead th').removeClass('asc desc');
th.addClass(sortType);
var tbody = table.find("tbody");
var rows = tbody.find("tr");
rows.sort(function(a, b) {
var A = $(a).find("td").eq(colIndex).text();
var B = $(b).find("td").eq(colIndex).text();
return cmp(A,B) * (sortType === 'asc' ? -1 : 1);
});
$.each(rows, function(index, ele) {
tbody.append(ele);
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border="1" class="sortable-table">
<thead>
<tr>
<th data-column="0">Firstname</th>
<th data-column="1">Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mark</td>
<td>Red</td>
</tr>
<tr>
<td>Nick</td>
<td>Sid</td>
</tr>
<tr>
<td>Alex</td>
<td>Nirv</td>
</tr>
</tbody>
</table>
Update
Added
var table = $(this).closest("table");
table.find('thead th').removeClass('asc desc');
th.addClass(sortType);
Related
I have a problem to sort a table in ascending and descending order. Need your help!!! I have numbers from 0,0000000052€ till 31.939€. Here is my tablesort.js, how can i solve this problem any ideas. My target is to sort the table in ascending and descending order when i click the headers. For all help i am very grateful.
$(document).ready(function () {
$('th').each(function (col) {
$(this).hover(
function () {
$(this).addClass('focus');
},
function () {
$(this).removeClass('focus');
}
);
$(this).click(function () {
if ($(this).is('.asc')) {
$(this).removeClass('asc');
$(this).addClass('desc selected');
sortOrder = -1;
} else {
$(this).addClass('asc selected');
$(this).removeClass('desc');
sortOrder = 1;
}
$(this).siblings().removeClass('asc selected');
$(this).siblings().removeClass('desc selected');
var arrData = $('table').find('tbody >tr:has(td)').get();
arrData.sort((a, b) => {
var val1 = $(a).children('td').eq(col).text().toUpperCase();
parseFloat(val1);
var val2 = $(b).children('td').eq(col).text().toUpperCase();
parseFloat(val2);
if ($.isNumeric(val1) && $.isNumeric(val2))
return sortOrder == 1 ? val1 - val2 : val2 - val1;
else return val1 < val2 ? -sortOrder : val1 > val2 ? sortOrder : 0;
});
$.each(arrData, function (index, row) {
$('tbody').append(row);
});
});
});
});
//Example ejs File
<% layout('layouts/boilerplate') -%>
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col">
<table
id="myTable"
class="mdl-data-table table table-sortable mdl-js-data-table mdl-data-table mdl-shadow"
>
<thead>
<tr>
<th class="mdl-data-table__header-cell--sorted">Kurs</th>
</tr>
</thead>
<tbody id="coinTable">
<tr>
<td class="current_price">0,0000234</td>
</tr>
<tr>
<td class="current_price">9,99</td>
</tr>
<tr>
<td class="current_price">31780,86</td>
</tr>
<script src="javascripts/tablesort.js"></script>
</tbody>
</table>
</div>
</div>``
Ok thanks for help. I found a way that works for me. Sure it is not the best way but it works fine and fast. I optimized my tablesort, here my solution.
var val1 = $(a)
.children('td')
.eq(col)
.text()
.toUpperCase()
.replace(/[.]/g, '')
.replace(/[_]/g, '0.0')
.replace(/[,]/g, '.');
var val3 = parseFloat(val1);```
I have an HTML table that has some static data and some from MySQL. It is currently filtering properly, what I need help is adding the "yes" and "no" selections to the selection list. These are just test values, they are being read from MySQL. I am unable to figure out what values to insert here to add values from MySQL to the selection list. Any assistance will be appreciated! Thank you
.append($table.find('tbody tr')
<!DOCTYPE html>
<html lang="en">
<link rel="stylesheet" href="/stylesheets/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<head>
<meta charset="utf-8">
<title>Filter</title>
</head>
<script>
$.ajax({
url: 'http://localhost:7003/getTable',
type: "get",
dataType: "json",
success: function(data) {
drawTable(data);
}
});
function drawTable(data) {
for (var i = 0; i < data.length; i++) {
drawRow(data[i]);
}
}
</script>
</body>
</html>
<table id="myTable" class="table table-striped">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th class="dropdown-header">Age</th>
<th>Email</th>
<th class="dropdown-header">Gender</th>
<th class="dropdown-header">Term</th>
<th class="dropdown-header">Enrolled</th>
</tr>
</thead>
<tbody>
<script>
function drawRow(rowData) {
var row = $("<tr />")
$("#myTable").append(row);
row.append($('<td>' + rowData.County + '</td>'));
row.append($('<td>' + rowData.County + '</td>'));
row.append($('<td>').attr('data-field-name', 'age').text(rowData.County));
row.append($('<td>' + rowData.County + '</td>'));
row.append($('<td data-field-name="gender">' + rowData.County + '</td>'));
row.append($('<td data-field-name="term">' + rowData.County + '</td>'));
row.append($('<td data-field-name="enrolled">' + rowData.County + '</td>'));
}
</script>
<tr>
<td>John</td>
<td>Smith</td>
<td data-field-name="age">15</td>
<td>123</td>
<td data-field-name="gender">Male</td>
<td data-field-name="term">Summer2017</td>
<td data-field-name="enrolled">Fall2018</td>
</tr>
<tr>
<td>Jane</td>
<td>Doe</td>
<td data-field-name="age">16</td>
<td>456</td>
<td data-field-name="gender">Female</td>
<td data-field-name="term">Fall2018</td>
<td data-field-name="enrolled">Fall2019</td>
</tr>
<tr>
<td>Bobby</td>
<td>Adams</td>
<td data-field-name="age">15</td>
<td>789</td>
<td data-field-name="gender">Male</td>
<td data-field-name="term">Spring2019</td>
<td data-field-name="enrolled">Fall2018</td>
</tr>
<tr>
<td>Sarah</td>
<td>Lee</td>
<td data-field-name="age">15</td>
<td>456</td>
<td data-field-name="gender">Female</td>
<td data-field-name="term">Fall2018</td>
<td data-field-name="enrolled">Fall2018</td>
</tr>
</tbody>
</table>
<script>
(function($) {
$.fn.tableFilterHeaders = function(filterFn) {
this.each((index, header) => {
let $header = $(header),
$table = $header.closest('table'),
text = $header.text(),
colIndex = $header.closest('th').index(),
fieldName = $header.attr('data-field-name') || text.toLowerCase(),
$select = $('<select>')
.data('fieldName', fieldName)
.append($('<option>').text(text).val('').prop('disabled', true))
.append($('<option>').text('All').val('all'))
.append($table.find('tbody tr')
.toArray()
.map(tr => {
return $(tr).find(`td:eq(${colIndex})`).text();
})
.filter(text => text.trim().length > 0)
.sort()
.filter((v, i, a) => a.indexOf(v) === i)
.map(text => {
return $('<option>').text(text).val(text);
}));
$header.empty().append($select.val('').on('change', filterFn));
});
};
$.fn.initRowClasses = function(oddCls, evenCls) {
this.find('tbody tr').each(function(i) {
$(this).toggleClass(oddCls, i % 2 == 0).toggleClass(evenCls, i % 2 == 1);
});
};
$.fn.updateRowClasses = function(oddCls, evenCls) {
this.find('tbody tr:visible:even').addClass(oddCls).removeClass(evenCls);
this.find('tbody tr:visible:odd').addClass(evenCls).removeClass(oddCls);
};
})(jQuery);
$('#myTable').initRowClasses('odd', 'even');
$('.dropdown-header').tableFilterHeaders(filterText);
function filterText(e) {
let $filter = $(e.target),
$table = $filter.closest('table'),
$filters = $table.find('.dropdown-header select'),
filterObj = $filters.toArray().reduce((obj, filter) => {
let $filter = $(filter);
return Object.assign(obj, { [$filter.data('fieldName')] : $filter.val() });
}, {});
if ($filter.val() === 'all') {
$filter.val('')
}
$table.find('tbody tr').each(function() {
$(this).toggle($(this).find('td').toArray().every(td => {
let $td = $(td), fieldName = $td.attr('data-field-name');
if (fieldName != null) {
return filterObj[fieldName] === null ||
filterObj[fieldName] === '' ||
filterObj[fieldName] === 'all' ||
filterObj[fieldName] === $td.text();
}
return true;
}));
});
$table.updateRowClasses('odd', 'even');
}
</script>
Not showing yes and no options.
Your filtering logic and odd/even row coloring logic is called BEFORE the data returns from your ajax even though it appears AFTER on the page/code. This is how async methods work.
You need to call the header and coloring logic inside the drawTable() function after, of course, you are done drawing the table... like this:
function drawTable(data) {
for (var i = 0; i < data.length; i++) {
drawRow(data[i]);
}
$('#myTable').initRowClasses('odd', 'even');
$('.dropdown-header').tableFilterHeaders(filterText);
}
Make sure to remove the foloowing 2 lines of code:
$('#myTable').initRowClasses('odd', 'even');
$('.dropdown-header').tableFilterHeaders(filterText);
from your code, wherever elsewhere they appear. Leaving them will not break anything, it will just run multiple times unnecessary.
I'm trying to implement a footerCallback in DataTables that computes a conditional sum of each column, based on a cell that's in a different column in the same row. Here's a demo of my setup: https://jsfiddle.net/rantoun/552y9j90/13/
HTML:
<table id="table1">
<thead>
<tr>
<th>Fruit</th>
<th>sumCondition</th>
<th># Eaten</th>
<th># Remaining</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th align="center">Count</th>
<th align="left"></th>
<th align="left"></th>
</tr>
</tfoot>
<tbody>
<tr>
<td>Apples</td>
<td>Use</td>
<td>3</td>
<td>8</td>
</tr>
<tr>
<td>Oranges</td>
<td>Use</td>
<td>6</td>
<td>5</td>
</tr>
<tr>
<td>Bananas</td>
<td>Ignore</td>
<td>2</td>
<td>9</td>
</tr>
</tbody>
</table>
jQuery:
$("#table1").DataTable({
"paging": false,
"searching": false,
"info": false,
"footerCallback": function ( row, data, start, end, display ) {
var columns = [2, 3];
var api = this.api();
_.each(columns, function(idx) {
var total = api
.column(idx)
.data()
.reduce(function (a, b) {
return parseInt(a) + parseInt(b);
}, 0)
$('tr:eq(0) th:eq('+idx+')', api.table().footer()).html(total);
})
}
});
Specifically, my goal is for the footerCallback to only sum the rows where "Ignore" is NOT in the Condition column. Hopefully this is clear and any help is appreciated.
I solved this by getting the current index of the summing value in the reduce function and then using the index to access the respective value in the condition cell. Below is the new jQuery code:
$('#table1').DataTable({
'paging': false,
'searching': false,
'info': false,
'footerCallback': function (row, data, start, end, display) {
var columns = [2, 3];
//console.log(data);
var api = this.api();
// Get sumCondition and put in array
_.each(columns, function (idx) {
var total = api
.column(idx)
.data()
.reduce(function (a, b) {
// Find index of current value for accessing sumCondition value in same row
var cur_index = api.column(idx).data().indexOf(b);
if (api.column(1).data()[cur_index] != 'Ignore') {
return parseInt(a) + parseInt(b);
} else {
return parseInt(a);
}
}, 0);
$('tr:eq(0) th:eq(' + idx + ')', api.table().footer()).html(total);
});
},
});
Working Fiddle: https://jsfiddle.net/rantoun/552y9j90/14/
There is a problem when rows in column have identical numbers. Where "if" statement is not applied. Base on this topic and answer: Jquery Datatable sum conditional footer callback not displaying correct result
HTML:
<table id="table1">
<thead>
<tr>
<th>Fruit</th>
<th>sumCondition</th>
<th># Eaten</th>
<th># Remaining</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th align="center">Count</th>
<th align="left"></th>
<th align="left"></th>
</tr>
</tfoot>
<tbody>
<tr>
<td>Apples</td>
<td>Use</td>
<td>3</td>
<td>8</td>
</tr>
<tr>
<td>Oranges</td>
<td>Use</td>
<td>6</td>
<td>5</td>
</tr>
<tr>
<td>Bananas</td>
<td>Ignore</td>
<td>6</td>
<td>9</td>
</tr>
</tbody>
</table>
jQuery:
$("#table1").DataTable({
"paging": false,
"searching": false,
"info": false,
"footerCallback": function ( row, data, start, end, display ) {
var columns = [2, 3];
//console.log(data);
var api = this.api();
// Get sumCondition and put in array
_.each(columns, function(idx) {
var total = total = api.data().reduce(function(a, b) {
var prev = 0;
var next = 0;
if (Array.isArray(a)) {
if (a[1] !== "Ignore") {
prev = parseInt(a[idx]);
}
} else {
prev = a;
}
if (b[1] !== "Ignore") {
next = parseInt(b[idx]);
}
return prev + next;
}, 0)
$('tr:eq(0) th:eq('+idx+')', api.table().footer()).html(total);
})
}
});
Working Fiddle: https://jsfiddle.net/79fx6aqg/1/
Stuck, I need to sort a table based off a custom attribute in the table row for each level then based off the text in the td itself. So for all levels go through and sort a higher level
So I have:
<table>
<tbody>
<tr data-level="1" data-parent="0"><td>3</td></tr>
<tr data-level="1" data-parent="0"><td>1</td></tr>
<tr data-level="2" data-parent="1"><td>1b</td></tr>
<tr data-level="2" data-parent="1"><td>1c</td></tr>
<tr data-level="2" data-parent="1"><td>1a</td></tr>
<tr data-level="1" data-parent="0"><td>2</td></tr>
</tbody>
</table>
my expectation would look like this:
<table>
<tbody>
<tr data-level="1" data-parent="0"><td>1</td></tr>
<tr data-level="2" data-parent="1"><td>1a</td></tr>
<tr data-level="2" data-parent="1"><td>1b</td></tr>
<tr data-level="2" data-parent="1"><td>1c</td></tr>
<tr data-level="1" data-parent="0"><td>2</td></tr>
<tr data-level="1" data-parent="0"><td>3</td></tr>
</tbody>
</table>
I have tried this:
function sortMultilevel(level){
var $sort = this;
var $table = $('table');
var $rows = $('tbody > tr[data-level="'+level+'"]',$table);
$rows.sort(function(a, b){
var keyA = $('td',a).text();
var keyB = $('td',b).text();
if($($sort).hasClass('asc')){
return (keyA > keyB) ? 1 : 0;
} else {
return (keyA < keyB) ? 0 : 1;
}
});
$.each($rows, function(index, row){
$table.append(row);
});
}
function doMultilevelSort(){
if($("tr").length > 0){
$("tr").not(".sorted").each(function(){
$(this).addClass("sorted");
var level = $(this).attr("data-level");
sortMultilevel(level);
console.log("sorting: " + level);
});
}
}
doMultilevelSort(); // call the function
I may be way over thinking this. If I am I am your student and I am all ears. Appreciate the look.
DEMO
$('#sort').click(function (e) {
var $table = $('#sort_me');
var $rows = $('tbody > tr', $table);
$rows.sort(function (a, b) {
var keyA = $('td', a).text();
var keyB = $('td', b).text();
return (keyA > keyB) ? 1 : 0;
});
$.each($rows, function (index, row) {
$table.append(row);
});
e.preventDefault();
});
I have knockout binding on table with columns. I was trying to achieve table sorting for each column.
The view looks like:
<table id="notes" class="notes_table">
<tr class="head">
<th data-bind='click: function() { SortItems("CreatedDate")}'>
<span>Date</span>
</th>
<th data-bind='click: function() { SortItems("Type")}'>
<span>Type</span>
</th>
<th data-bind='click: function() { SortItems("Category")}'>
<span>Category</span>
</th>
<th data-bind='click: function() {SortItems("AddedBy")}'>
<span>Added by</span>
</th>
</tr>
<tbody data-bind="template: { name: 'StudentNote', foreach: notes }"></tbody>
</table>
<script type="text/html" id="StudentNote">
<tr class="even">
<td><span data-bind="text: CreatedDate"></span></td>
<td><span data-bind="text: Type"></span></td>
<td><span data-bind="text: Category"></span></td>
<td><span data-bind="text: AddedBy"></span></td>
</tr>
</script>
and the javascript is like:
function notesViewModel() {
var _this = {};
_this.colName = "CreatedDate";
_this.sortOrder = "desc";
_this.notes = ko.observableArray();
_this.SortItems = function (ColumnName) {
var newNotes = _this.notes();
if (_this.sortOrder === "desc") {
this.notes(newNotes.sort(notesViewModel._getSortFunction = function (a, b) {
_this.sortOrder = "asc";
return a[ColumnName] < b[ColumnName] ? -1 : 1;
}));
} else {
this.notes(newNotes.sort(notesViewModel._getSortFunction = function (a, b) {
_this.sortOrder = "desc";
return a[ColumnName] > b[ColumnName] ? -1 : 1;
}));
}
};
ko.applyBindings(_this, $("body").get(0));
return _this;
Even though it does sorting, it just switches between ascending and descending sort on each of the column, but not recognises which column it is sorting.. How to do sorting by each column..
Try this:
function notesViewModel() {
var _this = {};
_this.colName = "CreatedDate";
_this.sortOrder = 1;
_this.notes = ko.observableArray();
_this.SortItems = function (ColumnName) {
if(ColumnName == _this.colName)
_this.sortOrder = _this.sortOrder * -1;
else
_this.colName = ColumnName;
_this.notes.sort(function (a, b) {
return (a[ColumnName] < b[ColumnName] ? -1 : 1) * _this.sortOrder;
});
};
ko.applyBindings(_this, $("body").get(0));
return _this;
}