Having trouble sorting table with JQuery sort method - javascript

I am using PHP and JQuery to build a data table, I am using jQuery's .sort() method to sort the table. However, it is not working as excepted.
For the text fields, nothing is changed (except that the rows are rendered again).
For the numeric fields, the table sorts in a really weird manner.
Clearly the ID does not make sense like this? Here is my JavaScript code:
const rows = tbody.find('tr').sort((a, b) => {
let first, second;
if ($(e.target).attr('data-order') === 'asc') {
second = a;
first = b;
$(e.target).attr('data-order', 'desc');
}
else {
second = b;
first = a;
$(e.target).attr('data-order', 'asc');
}
const firstTd = $(first).children().eq($(e.target).attr('data-index') - 1).text();
const secondTd = $(second).children().eq($(e.target).attr('data-index') - 1).text();
// Take care of numbers
try {
return firstTd - secondTd;
}
catch (e) {
// Value is string
const value = firstTd.localeCompare(secondTd, "en");
return value;
}
}).appendTo(tbody);
Here is the source code for my table (I just added two rows):
<table class="table">
<thead>
<tr>
<th>
<button class="btn btn-light data-table-sort"
data-index="1" data-order="asc">
id <span class="fa fa-sort" aria-hidden="true"></span>
</button>
</th>
<button class="btn btn-light data-table-sort"
data-index="3" data-order="asc">
Text <span class="fa fa-sort" aria-hidden="true"></span>
</button>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>72</td>
<td>af7c16d8f1</td>
<td>2021-11-26 06:16:55</td>
<td>
<form method="POST">
<input type="hidden" name="id" value="72">
<input type="submit" name="action" value="Details"
class="btn btn-info">
<input class="btn btn-primary" name="action"
type="submit" value="Edit">
<input data-id="72"
class="btn btn-danger data-table-delete"
name="action" value="Delete" type="submit">
</form>
</td>
</tr>
<tr>
<td>7</td>
<td>bedd6af3ed</td>
<!-- The same form as previous row -->
</td>
</tr>
</tbody>
</table>
I have made sure that tbody contains the correct value. I am also selecting the correct column. The table has to be dynamic and reusable so I can't hardcode everything.
Note: I have already tried appending rows to the table instead of the <tbody> element.

You probably need to detach elements since you reattach it again later:
tbody.find('tr').detach().sort...
This may need readjustments depending on how jQuery arrays are returned but detaching elements is the main idea and other things should be pretty trivial.

Related

Delete row using inout field

I am creating table, and want to remove row by id using input field. (if input field matches with id then the row must be deleted)
can not figure it out.
Your help is much appreciated
`
<body onload="addRow()">
<table id="myTable" style="display:none">
<tr>
<th class="borderless">ID</th>
<th>Name</th>
<th>Username</th>
<th>Gender</th>
<th>Country</th>
</tr>
<tbody id="myTableBody">
</tbody>
</table>
<button type="button" id="buttonShow" onclick="showTable()">Show Table</button>
<button type="button" id="buttonAdd" onclick="addRow()" disabled>Add a new row</button>
<br>
<label>
<input class="input1" type="text" name="todoTags"/>
<button class="dellbtn" id="buttonDell"onclick="delRow()" disabled>Delete row</button>
</label>
`
`
function showTable(){
document.getElementById("myTable").style.display = "block";
document.getElementById("buttonAdd").disabled = false;
document.getElementById("buttonDell").disabled = false;
}
const btn = document.querySelector('.dellbtn')
const userTags = []
`
Here is my: JSfiddle
What you could do is changing the addRow() method so it adds a data-attribute to each row, in the <tr>. You can achieve this goal by adding this when creating the row :
row.setAttribute("row-id", tr.length - 1);
Then, when you want to delete it, you can simply search the
row that has the data-attribute that you just input. And it will look like this :
function delRow() {
const value = document.getElementById("valueToDelete").value;
document.querySelector('[row-id="' + value + '"]').remove();
}
I created a fork to your JSFiddle that you can check right here.
Hope it helps ! Good luck :)
Not exactly what you're asking, but this method might work better for you. It uses a delete button for each row so you can decide which one to delete. Then it uses a delegate listener to enable the delete buttons
const table = document.querySelector('#theTable tbody');
let c = 1
const addRow = () => {
table.innerHTML += `<tr><td>${c} data</td><td>${c} data</td><td><button class='delete'>delete</button></td></tr>`;
c++
}
// delegate listener for the delete button
table.addEventListener('click', e => {
if (e.target.classList.contains('delete')) {
e.target.closest('tr').remove();
}
})
<table id='theTable'>
<th>
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td></td>
</tr>
</th>
<tbody>
</tbody>
</table>
<button onclick='addRow()'>Add Row</button>

jQuery DataTables inline editing of empty fields

So far this is my set-up of DataTables used in my own app:
But the input fields are not that UI-friendly or pleasing to look at. And so I happen to stumble upon this feature of DataTable, which is Inline Editing, and now instead of those unpleasant-looking input fields, I want to have the fields similar to the Inline Editing functionality of Data Tables, with the exception that there are no values "to be edited", just empty fields with placeholder texts.
Is that even possible? I tried to trace the example and it seems to make use of some DataTable.Editor, but I can't seem to grasp how to implement an empty field one instead of a "editable field with default contents" (like the examples found in the documentations).
Any help? Also, if appreciated, I'd like to have the Total Load to automatically be computed whenever the No. of Units and Power Rating is filled up. It is simply the (No. of Units * Power Rating).
Here's my initial code setup:
The table.js contains the initialization of the DataTables,
whilst globs.js contains the ajax query with the corresponding creation of rows in the table.
I just pasted the codes that are relevant to my questions, and disregarded the others (jQuery actions of the buttons and etc)
table.js (Initialized jQuery codes)
$(function() {
var table = $('#costcenter_table').DataTable({
"columnDefs": [
{
"targets": 4,
"visible": false,
"searchable": false,
"sortable": false,
"width": "75px"
}
]
});
});
globs.js (contains functions with Ajax queries)
function show_cost_tables(costcenter){
$.ajax({
url: '/costcenter/' + costcenter,
type: 'GET',
dataType: 'json',
async: true,
success: function(res){
if(res.status === "success"){
$('#costcenter_table').DataTable().clear();
for(var i = 0; i < res.count; i++){
var entry = res.data[i];
var buttons = '<div style="text-align: center;"><button class="btn btn-xs btn-primary add-unit"><span class="fa fa-plus"></span></button> ' +
'<button class="btn btn-xs btn-warning remove-unit"><span class="fa fa-minus"></span></button> ' +
'<button class="btn btn-xs btn-danger delete-load"><span class="fa fa-close"></span></button></div>';
$('#costcenter_table').DataTable().row.add([entry.name, entry.unit_count, entry.power_rating, entry.total_load, buttons]).draw();
}
} else {
console.log(res.status + " login details.");
$('#warning-login').modal('toggle');
}
}
});
}
table.html (the input fields at the footer here should be gone and be replaced by that Inline Edit fields of DataTables)
<table id="costcenter_table" class="table table-bordered table-striped" cellspacing="0" width="100%">
<thead>
<tr>
<th>Load</th>
<th>No. of Units</th>
<th>Power Rating</th>
<th>Total Load</th>
<th></th>
</tr>
</thead>
<tbody id ="costcenter_data">
</tbody>
<tfoot id="add-new-load" style="display: none;">
<tr>
<form id="new-load" role="form">
<th><input id="name" name="name" type="text" placeholder="Load Name"><br><br><input id="brand" name="brand" type="text" placeholder="Brand"></th>
<th><input id="unit_count" name="unit_count" type="text" placeholder="No. of Units"><br><br><input id="location" name="location" type="text" placeholder="Location"></th>
<th><input id="power_rating" name="power_rating" type="text" placeholder="Power Rating"></th>
<th><input id="total_load" placeholder="Total Load" type="text" disabled></th>
<th><button id="add-load" class="btn btn-primary add-load" >Add New Load</button></th>
</form>
</tr>
</tfoot>
</table>
PS. Please try to ignore the last two input fields (Brand and Location), they are supposed to be dropdowns and browse buttons respectively.

How to use closest() in jQuery

I am trying to get value of a text box by using closest().
My form contains dynamically created table with one text box and button in each row . I want to show the value of text box in button click event.
Table format is given below
<table id="dataarea" class="table">
<tbody>
<tr>
<td>jaison<td>
<td class="amountclm">
<input id="txtamt" class="amtcls" type="text" value="100">
</td>
<td>
<button id="schedule" class="schbtn btn btn-primary" type="button" onclick="UpdateSchedule()">Confirm</button>
</td>
</tr>
<tr>
<td>ARUN<td>
<td class="amountclm">
<input id="txtamt" class="amtcls" type="text" value="500">
</td>
<td>
<button id="schedule" class="schbtn btn btn-primary" type="button" onclick="UpdateSchedule()">Confirm</button>
</td>
</tr>
</tbody>
</table>
I have written one javascript code (given below) . when the code will execute, it return a null value.
Function
function UpdateSchedule() {
var amt2 = $(this).closest('td.amountclm').find('.amtcls').val();
alert( amt2);
}
Please let me know why am not getting the null value.
jQuery .closest() goes up from the current element until it finds what you give it to find. jQuery .find() goes down until it finds what you give it to find.
This does the trick:
http://jsfiddle.net/6T3ET/
id must be unique, you need to use class instead:
<button class="schedule schbtn btn btn-primary" type="button">
<input class="amtcls txtamt" type="text" value="500">
an use .click() instead of inline onClick handler since you're using jQuery. So you can do:
$('.schedule').click(function() {
var amt2 = $(this).closest('td').prev('td.amountclm').find('.amtcls').val();
alert( amt2);
});
Please note that the .amountclm input is the child of a td whose is the immediate previous sibling of parent td of your clicked .schedule button.
Try this
replace 'this' with the 'schedule_' class attribute
function UpdateSchedule() {
var amt2 = $(".schedule_").closest('td.amountclm').find('.amtcls').val();
alert( amt2);
}
HTML :
<table id="dataarea" class="table">
<tbody>
<tr>
<td>jaison<td>
<td class="amountclm">
<input id="txtamt" class="amtcls" type="text" value="100">
</td>
<td>
<button id="schedule" class="schbtn btn btn-primary schedule_" type="button" onclick="UpdateSchedule()">Confirm</button>
</td>
</tr>
<tr>
<td>ARUN<td>
<td class="amountclm">
<input id="txtamt" class="amtcls" type="text" value="500">
</td>
<td>
<button id="schedule" class="schbtn btn btn-primary schedule_" type="button" onclick="UpdateSchedule()">Confirm</button>
</td>
</tr>
</tbody>
</table>
or try with Jquery as
$(document).on('click', '.schedule_',function(){
var amt2 = $(this).closest('td.amountclm').find('.amtcls').val();
alert( amt2);
} );
Use .parent() and .prev() in jquery for this contradiction
$(".schbtn").click(function() {
var amt2 = $(this).parent().prev("td.amountclm").find('.amtcls').val();
alert( amt2);
});
Fiddle
First of all .closest() is not plain JavaScript. It's a function from jQuery.
http://api.jquery.com/closest/
and it traverses up in the element hierachy. And td.amountclm is not in the element hierachy of the button#schedule.
Therefore .closest wont find it.

Can't show a display none inside a table

I'm having a problem with style.display.
I want to show a submit box (which is inside a table) when I change a number and it's not working.
Here's the code:
<head>
<script>
function changedisp(Section){
if (Section.style.display=="none"){
Section.style.display=""
}
else{
Section.style.display="none"
}
}
function raisenumber(s,s1) {
var x;
x = document.getElementById(s).innerHTML;
document.getElementById(s).innerHTML = x*1 + 1;
changedisp(s1);
}
</script>
</head>
<body>
<table>
<tr>
<th><b>ID</b></th>
<th><b>number</b></th>
<th><b>buttoon</b></th>
<th><b>submit</b></th>
</tr>
<form action="modifica_inventario.php" method="post">
<tr>
<td> <b id="cell1A">item number1</b></td>
<td> <b id="cell2A">10</b></td>
<td> <button id="cell3A" type="button" onClick=raisenumber("cell2A","cell4A")>+1</button></td>
<td> <b id="cell4A" style="display: none;"> <input type="submit" value="submit"/></b> &nbsp </td>
</tr>
<tr>
<td> <b id="cell1B">item number1</b></td>
<td> <b id="cell2B">10</b></td>
<td> <button id="cell3B" type="button" onClick=raisenumber("cell2B","cell4B")>+1</button></td>
<td> <b id="cell4B" style="display: none;"> <input type="submit" value="submit"/></b> &nbsp </td>
</tr>
</form>
</table>
</body>
Well I don't know why it's not showing on and off the submit button every time a press the button...
I also tried replacing
changedisp(s1);
by
s1.style.display="";
Any comments about this?
You need to pass the actual element you are trying to hide and show to your changedisp function. In your code you are just passing the ID name of the element. Try this...
function raisenumber(s,s1) {
var x;
x = document.getElementById(s).innerHTML;
document.getElementById(s).innerHTML = x*1 + 1;
changedisp(document.getElementById(s1));
}
notice changedisp(document.getElementById(s1)); passes the actual element instead of just the ID.
You need to set it to a display type explicitly:
Section.style.display="inline";
Or inline-block, or block, or whatever you want.
A <B> is by default display:inline, so trying setting to "inline" not the empty string, "".

Combines table filters in jquery

I have a table with different places, and implemented some simple buttons that allow you to filter the list. First filter is location (north, east, central, south, west) which is based on postcode. Another filter is on "impress". This shows you only the places that have have 4 or higher value in the column. Filters work great separately, but not together. The result that I am after is when I press "West" is shows me the places in "West, when I then click impress, I expect to see the places in west with a 4 or 5 score for impress.
JSFiddle here
$('.table td.postcode').each(function() {
var cellText = $(this).html();
var locationString = cellText.substring(0,2);
if (locationString.indexOf('W') > -1){
$(this).parent().addClass('west');
}
if (locationString.indexOf('C') > -1){
$(this).parent().addClass('central');
}
if (locationString.indexOf('E') > -1){
$(this).parent().addClass('east');
}
if (locationString.indexOf('S') > -1){
$(this).parent().addClass('south');
}
if (locationString.indexOf('N') > -1){
$(this).parent().addClass('north');
}
});
$("input[name='filterStatus'], select.filter").change(function () {
var classes = [];
$("input[name='filterStatus']").each(function() {
if ($(this).is(":checked")) {
classes.push('.'+$(this).val());
}
});
if (classes == "") {
// if no filters selected, show all items
$("#StatusTable tbody tr").show();
} else {
// otherwise, hide everything...
$("#StatusTable tbody tr").hide();
// then show only the matching items
rows = $("#StatusTable tr").filter(classes.length ? classes.join(',') : '*');
if (rows.size() > 0) {
rows.show();
}
}
});
$("input[name='impressStatus']").change(function(){
var classes = [];
$("input[name='impressStatus']").each(function() {
if ($(this).is(":checked")) {
classes.push('.'+$(this).val());
}
});
if(classes == ""){
$("#StatusTable tbody tr").show();
}
else{
$(".table td.impress").each(function(){
if($(this).data("impress") >= 4){
$(this).parent().show();
}
else{
$(this).parent().hide();
}
});
}
});
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!--BUTTON FILTERS -->
<div class="btn-toolbar" role="toolbar" aria-label="...">
<div class="btn-group" style="" data-toggle="buttons">
<label class="btn btn-primary outline">
<input type="checkbox" name="filterStatus" value="north" autocomplete="off">North
</label>
<label class="btn btn-primary outline">
<input type="checkbox" name="filterStatus" value="east" autocomplete="off" class="radio">East
</label>
<label class="btn btn-primary outline">
<input type="checkbox" name="filterStatus" value="central" autocomplete="off" class="radio">Central
</label>
<label class="btn btn-primary outline">
<input type="checkbox" name="filterStatus" value="south"autocomplete="off" class="radio">South </label>
<label class="btn btn-primary outline">
<input type="checkbox" name="filterStatus" value="west" autocomplete="off" class="radio">West
</label>
</div><!-- button group -->
<label class="btn btn-primary outline">
<input type="checkbox" name="impressStatus" class="radio" aria-pressed="true" autocomplete="off">Impress her
</label>
</div><!-- btn toolbar-->
<!--TABLE -->
<table class="table" id="StatusTable">
<thead>
<tr>
<th data-sort="string" style="cursor:pointer">name</th>
<!-- <th>Description</th> -->
<th data-sort="string" style="cursor:pointer;">postcode</th>
<th data-sort="int" style="cursor:pointer;">price</th>
<th data-sort="int" style="cursor:pointer;">total</th>
<th data-sort="int" style="cursor:pointer;">impress</th>
<th colspan="4"></th>
</tr>
</thead>
<tbody>
<tr data-link="/places/1">
<td>Name of place 1</td>
<td class="postcode">NW1</td>
<td class="price" data-price='3'>3</td>
<td class="rating" data-rating='69'>69</td>
<td class="impress" data-impress='4'>4</td>
</tr>
<tr data-link="/places/2">
<td>Name of place 2</td>
<td class="postcode">E3</td>
<td class="price" data-price='4'>4</span></td>
<td class="rating" data-rating='89'>89</td>
<td class="impress" data-impress='5'>5</td>
</tr>
<tr data-link="/places/3">
<td>Name of place 3</td>
<td class="postcode">SW3</td>
<td class="price" data-price='2'>2</td>
<td class="rating" data-rating='51'>51</td>
<td class="impress" data-impress='3'>3</td>
</tr>
</tbody>
</table>
Code is probably not the most efficient, but it works :). Once I got this working, I want to add more filters.
(sorry for my bad english)
if these are the only filters you need, you can use two different type of filter:
hide via Javascript
hide via Css
if you use 2 types of filters the filters can work correctly without use a complex javascript code to manage a big number of different cases and combination:
I add a initial (on document load) control that check if a tr has the value impress cell >4, if has it add a new class: is_impress else add an other: no_impress.
$('.table td.impress').each(function(){
var _class = ($(this).data("impress") >= 4) ? "is_impress" : "no_impress";
$(this).parent().addClass(_class);
});
The code of filter by position is the same... but... I edit the filter by impress to add a class to table () when is active and take it off when isn't:
$("input[name='impressStatus']").change(function(){
(!$(this).is(":checked"))
? $("#StatusTable").removeClass("active_impress")
: $("#StatusTable").addClass("active_impress");
});
if the table has the class active_impress a css rules override the inline code of dispaly to hide all the row that haven't an impress >4:
#StatusTable.active_impress tr.no_impress{
display:none !important;
}
This type of filter override any other display modification until the checkbox stay checked.
I edit your fiddle:
https://jsfiddle.net/Frogmouth/gkba343L/1/
USE CSS to more filter
First change on load check, add price:
$('.table tbody tr').each(function(){
var _class = "";
_class += ($(this).find(".price").data("price") >= 2) ? "is_price " : "no_price ";
_class += ($(this).find(".impress").data("impress") >= 4) ? "is_impress " : "no_impress ";
console.log(_class);
$(this).addClass(_class);
});
Add an other handler to new filter:
$("input[name='priceStatus']").change(function(){
(!$(this).is(":checked"))
? $("#StatusTable").removeClass("active_price")
: $("#StatusTable").addClass("active_price");
});
add new selector to the css rule:
#StatusTable.active_impress tr.no_impress,
#StatusTable.active_price tr.no_price{
display:none !important;
}
This is the result:
https://jsfiddle.net/Frogmouth/gkba343L/3/
Optimize code to add more filter:
HTML filter button:
<label class="btn btn-primary outline">
<input type="checkbox" name="impress" class="cssFilter radio" aria-pressed="true" autocomplete="off">Impress her
</label>
use cssFilter to indicate that is a css filter button and use name attribute to define the name of the filter, than use this namespace into the css class:
.active_{name} .no_{name} .is_{name}
And use a generic handler:
$("input.cssFilter").change(function(){
var _name = $(this).attr("name");
console.log(_name);
(!$(this).is(":checked"))
? $("#StatusTable").removeClass("active_"+_name)
: $("#StatusTable").addClass("active_"+_name);
});
With this you can manage all the filter with an unique handler, remember to add the filter to onload check and the new selector for each new filter.
Fiddle:
https://jsfiddle.net/Frogmouth/gkba343L/4/

Categories

Resources