Sort rows in table - javascript

I'm trying to sort rows in alphabetical order based on which column header is clicked using jQuery. It works fairly fine when debugging except that it doesn't actually switch the rows in the HTML and so it doesn't display a sorted table on the webpage. I'm using Thymeleaf th:text to populate the table body rows but for the sake of this example, I hardcoded some values. You can run it here: https://jsfiddle.net/tg2khrd4
Javascript:
var table = $("#table");
$("#subject, #from, #id")
.wrapInner('<span title="sort this column"/>')
.each(function () {
var th = $(this),
thIndex = th.index(),
inverse = false;
th.click(function () {
table
.find("tbody")
.find("td")
.filter(function () {
return $(this).index() === thIndex;
})
.sort(
function (a, b) {
return $.text([a]) > $.text([b])
? inverse
? -1
: 1
: inverse
? 1
: -1;
},
function () {
// parentNode is the element we want to move
return this.parentNode;
}
);
inverse = !inverse;
});
});
HTML:
<table class="table table-hover" id="table" style="background-color:#fff;border: 1px solid #cccccc">
<thead style="background-color:#981e32;">
<tr>
<td class="tdsubj" id="id" style="padding:5px;">Id
</td>
<td class="tdsubj" id="subject" style="padding:5px;">
Subject
</td>
<td class="tdsubj" id="from" style="padding:5px;">
From
</td>
<td class="tdsubj" id="date" style="padding:5px;">
Date
</td>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Hello</td>
<td>Thor</td>
<td>2020-10-19</td>
</tr>
<tr>
<td>2</td>
<td>Dinos Suck</td>
<td>Meteor</td>
<td>2020-9-5</td>
</tr>
<tr>
<td>3</td>
<td>Big Ben won't stop ringing</td>
<td>The Queen</td>
<td>2020-8-19</td>
</tr>
</tbody>
</table>

Once the td sorted... You just have to loop throught it and append it's parent tr in the table...
var table = $("#table");
$("#subject, #from, #id")
// .wrapInner('<span title="sort this column"/>')
.each(function () {
var th = $(this),
thIndex = th.index(),
inverse = false;
th.click(function () {
let test = table
.find("tbody")
.find("td")
.filter(function () {
return $(this).index() === thIndex;
})
.sort(
function (a, b) {
return $.text([a]) > $.text([b])
? inverse
? -1
: 1
: inverse
? 1
: -1;
}
// That is useless...
/*function () {
// parentNode is the element we want to move
console.log(this.parentNode)
return this.parentNode;
}*/
);
// Added to demonstrate the sorting works
console.clear()
test.each(function () {
console.log(this.innerText);
});
// Now to apply the sorting on the DOM
// Find the tr containing it and append it to the table.
test.each(function () {
table.append($(this).parent("tr"))
});
inverse = !inverse;
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table table-hover" id="table" style="background-color:#fff;border: 1px solid #cccccc">
<thead style="background-color:#981e32;">
<tr>
<td class="tdsubj" id="id" style="padding:5px;">Id
</td>
<td class="tdsubj" id="subject" style="padding:5px;">
Subject
</td>
<td class="tdsubj" id="from" style="padding:5px;">
From
</td>
<td class="tdsubj" id="date" style="padding:5px;">
Date
</td>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Hello</td>
<td>Thor</td>
<td>2020-10-19</td>
</tr>
<tr>
<td>2</td>
<td>Dinos Suck</td>
<td>Meteor</td>
<td>2020-9-5</td>
</tr>
<tr>
<td>3</td>
<td>Big Ben won't stop ringing</td>
<td>The Queen</td>
<td>2020-8-19</td>
</tr>
</tbody>
</table>

Related

How to hide a table row or result if checkbox is checked AND if 0 result/count

I have this counter for word occurrence in the textarea. The problem is, I have a lot of items in the table, and so it can be distracting to include the zero results.
So what I'm hoping to achieve is, if the user checks the checkbox, it will not show the zero results anymore (preferably the whole row)..
Please see the code so far:
let textarea = $('#textarea3');
textarea.on('keyup', _ => counting());
function counting() {
var searchText = $('#textarea3').val();
let words = [];
words['1 sample'] = '#one';
words['2 sample'] = '#two';
words['3 sample'] = '#three';
words['4 sample'] = '#four';
words['5 sample'] = '#five';
words['6 sample'] = '#six';
for (const word in words) {
var outputDiv = $(words[word]);
outputDiv.empty();
let count = searchText.split(word).length - 1;
searchText = searchText.replaceAll(word,'');
outputDiv.append('<a>' + count + '</a>');
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="checkbox">
<label> Don't show zero results</label><br>
<button onclick="counting();">Count</button>
<table>
<thead>
<tr>
<th scope="col">Items</th>
<th scope="col">Count</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 sample</td>
<td><a id="one"></a></td>
</tr>
<tr>
<td>2 sample</td>
<td><a id="two"></a></td>
</tr>
<tr>
<td>3 sample</td>
<td><a id="three"></a></td>
</tr>
<tr>
<td>4 sample</td>
<td><a id="four"></a></td>
</tr>
<tr>
<td>5 sample</td>
<td><a id="five"></a></td>
</tr>
<tr>
<td>6 sample</td>
<td><a id="six"></a></td>
</tr>
</tbody>
</table>
<textarea id="textarea3" rows="5">
1 sample
2 sample
3 sample
5 sample
</textarea>
If the checkbox isn't checked, it should function as is and still show all results.
I've seen this post but I'm not really sure how to implement it to my own project. Show or hide table row if checkbox is checked
Thank you in advance for any help.
Consider the following.
$(function() {
var textarea = $('#textarea3');
var words = [];
$("table tbody tr").each(function(i, row) {
words.push({
term: $("td:eq(0)", row).text().trim(),
rel: "#" + $("a", row).attr("id"),
count: 0
});
});
function count() {
var searchText = textarea.val();
$.each(words, function(i, word) {
if (searchText.indexOf(word.term) >= 0) {
var re = new RegExp('(' + word.term + ')', 'gi');
word.count = searchText.match(re).length;
$(word.rel).html(word.count);
} else {
word.count = 0;
if (!$("#noShowZero").is(":checked")) {
$(word.rel).html(word.count);
} else {
$(word.rel).html("");
}
}
});
}
textarea.keyup(count);
$("#count-btn, #noShowZero").click(count);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="noShowZero" type="checkbox">
<label> Don't show zero results</label><br>
<button id="count-btn">Count</button>
<table>
<thead>
<tr>
<th scope="col">Items</th>
<th scope="col">Count</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 sample</td>
<td>
<a id="one"></a>
</td>
</tr>
<tr>
<td>2 sample</td>
<td>
<a id="two"></a>
</td>
</tr>
<tr>
<td>3 sample</td>
<td>
<a id="three"></a>
</td>
</tr>
<tr>
<td>4 sample</td>
<td>
<a id="four"></a>
</td>
</tr>
<tr>
<td>5 sample</td>
<td>
<a id="five"></a>
</td>
</tr>
<tr>
<td>6 sample</td>
<td>
<a id="six"></a>
</td>
</tr>
</tbody>
</table>
<textarea id="textarea3" rows="5">
1 sample
2 sample
3 sample
5 sample
</textarea>
When the User:
Enters text in the textbox
Clicks the checkbox
Clicks the Button
then count function is executed.
Count will review all the words and look for specific keywords. A count of them is also retained, as well as element relationship to show that count.
Using Regular Expressions, we can search for the words in the text and count them using .match(). It returns an Array of the matches. You could also use .replace(), to remove them.

universal javascript selector to interact on all html element

I started on that http://jsfiddle.net/DRFBG/
And if I add tables so mytable1, mytable2,...
<table id="mytable1" border="1">
<tr><th>Column1</th><th>Column2</th><th>Column3</th><th>Column4</th></tr>
<tr class="data"><td>1st</td><td>1.1</td><td></td><td>1</td></tr>
<tr class="data"><td>2nd</td><td>2.01</td><td></td><td>2</td></tr>
<tr class="data"><td>3rd</td><td>3.001</td><td></td><td>3</td></tr>
<tr class="data"><td>4th</td><td>4.01</td><td></td><td>4</td></tr>
</table>
<table id="mytable2" border="1">
<tr><th>Column1</th><th>Column2</th><th>Column3</th><th>Column4</th></tr>
<tr class="data"><td>1st</td><td>1.1</td><td>1</td><td></td></tr>
<tr class="data"><td>2nd</td><td>2.01</td><td>2</td><td></td></tr>
<tr class="data"><td>3rd</td><td>3.001</td><td>3</td><td></td></tr>
<tr class="data"><td>4th</td><td>4.01</td><td>4</td><td></td></tr>
</table>
How could I uniform my javascript code for all tables?
I've already tried passing by table[div^=mytable]*, but the problem is the second selector in the function.
So any ideas please? Thank you? Sorry for my english
By the way, the code is to remove th with empty td for each table
$('#mytable2 th').each(function(i) {
var remove = 0;
var tds = $(this).parents('table').find('tr td:nth-child(' + (i + 1) + ')')
tds.each(function(j) { if (this.innerHTML == '') remove++; });
if (remove == ($('#mytable2 tr').length - 1)) {
$(this).hide();
tds.hide();
}
});
One approach is, selecting tables first and get their id and after that, doing the approach of http://jsfiddle.net/DRFBG/ on each of them like the following:
$('table').each(function()
{
var tb_id = $(this).attr('id');
$('#'+tb_id+' th').each(function(i) {
var remove = 0;
var tds = $(this).parents('table').find('tr td:nth-child(' + (i + 1) + ')')
tds.each(function(j) { if (this.innerHTML == '') remove++; });
if (remove == ($('#'+tb_id+' tr').length - 1)) {
$(this).hide();
tds.hide();
}
});
});
Here is the working jsfiddle
To select all on your page you can use "table" selector.
So you'd need to use $('table2 th') instead of $('#mytable2 th')
One possible solution would be to loop through each column of each table, then check if there are any non-empty cells. If there is not, then you can safely remove() all the td and th within that column.
Note that the removal needs to be done last, otherwise it will affect the indexing of the following columns. You can do that by simply marking the cells to be removed with a class, and then selecting that class once all loops complete. Try this:
$('table').each(function() {
var $table = $(this);
var rows = $table.find('tr').length - 1; // -1 to account for the headings
$table.find('th').each(function(i, th) {
var $empty = $table.find(`td:nth-child(${i + 1}):empty`);
if ($empty.length == rows)
$empty.add(this).addClass('to-remove');
})
$table.find('.to-remove').remove();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="mytable1" border="1">
<tr>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
<th>Column4</th>
</tr>
<tr class="data">
<td>1st</td>
<td>1.1</td>
<td></td>
<td>1</td>
</tr>
<tr class="data">
<td>2nd</td>
<td>2.01</td>
<td></td>
<td>2</td>
</tr>
<tr class="data">
<td>3rd</td>
<td>3.001</td>
<td></td>
<td>3</td>
</tr>
<tr class="data">
<td>4th</td>
<td>4.01</td>
<td></td>
<td>4</td>
</tr>
</table>
<table id="mytable2" border="1">
<tr>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
<th>Column4</th>
</tr>
<tr class="data">
<td>1st</td>
<td>1.1</td>
<td>1</td>
<td></td>
</tr>
<tr class="data">
<td>2nd</td>
<td>2.01</td>
<td>2</td>
<td></td>
</tr>
<tr class="data">
<td>3rd</td>
<td>3.001</td>
<td>3</td>
<td></td>
</tr>
<tr class="data">
<td>4th</td>
<td>4.01</td>
<td>4</td>
<td></td>
</tr>
</table>

Sort rows based in ascending order using jQuery

This code works but checks only the first column. I want to check the 2nd column instead with the points. How do I alter it?
HTML Table:
<table width="100%">
<thead>
<tr>
<th width="60%">Name</th>
<th width="20%">School</th>
<th width="20%">Points</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="4"><h1>Event 1</h1></td>
<td>School1</td>
<td>74</td>
</tr>
<tr>
<td>School2</td>
<td>69</td>
</tr>
<tr>
<td>School3</td>
<td>71</td>
</tr>
<tr>
<td>School4</td>
<td>11</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td rowspan="4"><h1>Event 2</h1></td>
<td>School1</td>
<td>34</td>
</tr>
<tr>
<td>School5</td>
<td>29</td>
</tr>
<tr>
<td>School3</td>
<td>62</td>
</tr>
<tr>
<td>School7</td>
<td>15</td>
</tr>
</tbody>
</table>
jQuery:
var $tbody = $('#caltbl tbody');
$tbody.find('tr').sort(function (a, b) {
var tda = $(a).find('td:eq(0)').text();
var tdb = $(b).find('td:eq(0)').text();
// if a < b return 1
return tda > tdb ? 1
// else if a > b return -1
: tda < tdb ? -1
// else they are equal - return 0
: 0;
}).appendTo($tbody);
How do I got about this?
JSFIDDLE
EDIT: I'm sorry guys but my live code is different with rowspan being used. Is there a possibility to have this in ascending order so that the events are sorted differently?
eq(0) means you are using the first index. Change it to eq(1) so that it can consider the second index.
var tda = $(a).find('td:eq(1)').text();
var tdb = $(b).find('td:eq(1)').text();
You can sort the trs. Detach the td and insert to an array. Then append them back to each row
Add some class to simplify coding.
JSFIDDLE
HTML
<table width="100%">
<thead>
<tr>
<th width="60%">Name</th>
<th width="20%">School</th>
<th width="20%">Points</th>
</tr>
</thead>
<tbody>
<tr class="event1">
<td rowspan="4"><h1>Event 1</h1></td>
<td>School1</td>
<td class="point">74</td>
</tr>
<tr class="event1">
<td>School2</td>
<td class="point">69</td>
</tr>
<tr class="event1">
<td>School3</td>
<td class="point">71</td>
</tr>
<tr class="event1">
<td>School4</td>
<td class="point">11</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td rowspan="4"><h1>Event 2</h1></td>
<td>School1</td>
<td>34</td>
</tr>
<tr>
<td>School5</td>
<td>29</td>
</tr>
<tr>
<td>School3</td>
<td>62</td>
</tr>
<tr>
<td>School7</td>
<td>15</td>
</tr>
</tbody>
</table>
JavaScript
var $tbody = $(' tbody');
var array = [];
$tbody.find('.event1').sort(function (a, b) {
var tda = $(a).find('.point').text();
var tdb = $(b).find('.point').text();
return tda - tdb;
}).each(function (idx, tr) {
array.push($(tr).children('td').not('[rowspan]').detach());
});
$.each(array, function (idx, obj) {
$(obj).appendTo($('.event1:eq(' + idx + ')'));
});
The JavaScript only applies to event1. You can simply modify it for arbitrary events.
Change the index as Mayank Pandey said. And..
Since your second column is number, you can just return their difference.
var $tbody = $('#caltbl tbody');
$tbody.find('tr').sort(function (a, b) {
var tda = parseInt($(a).find('td:eq(1)').text(), 10); // always use the base number
var tdb = parseInt($(b).find('td:eq(1)').text(), 10);
return tda - tdb;
}).appendTo($tbody);

Changing text into an input and then back again on deselection

I have a button, when i click it, i transform its sibling into an input containing the text that it once held.
This works fine.
However, upon deselection of the input, I require it to go back to being text again, but the issue is, the const variable is being overwritten somehow?
Anyway, here is my current HTML and JS:
$(function() {
$(document).on("click", "table.table-striped > tbody > tr > td > a.copy-btn", function(e) {
e.preventDefault();
const $this = $(this),
text = $this.parent().parent().find("td.link").text();
$this.parent().parent().find("td.link").html(`<input type="text" class="form-control" value="${text}">`);
$this.parent().parent().find("td.link input").select();
if ($this.parent().parent().find("td.link input").blur()) {
$this.parent().parent().find("td.link").html(text);
}
});
return false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table table-striped">
<thead>
<tr>
<th class="title">link name</th>
<th class="title">Platform</th>
<th class="title">Link</th>
</tr>
</thead>
<tbody>
<tr>
<th class="body-text" scope="row">1</th>
<td class="body-text">Mark</td>
<td class="body-text link">Otto</td>
<td class="body-text">Copy
</td>
<td class="body-text">More
</td>
</tr>
<tr>
<th class="body-text" scope="row">1</th>
<td class="body-text">Otto</td>
<td class="body-text link">Mark</td>
<td class="body-text">Copy
</td>
<td class="body-text">More
</td>
</tr>
</tbody>
</table>
The line $this.parent().parent().find("td.link input").blur() will always return true.
It fires the blur event, and returns the jQuery collection.
If you wanted to check if the element was focused, the easiest would be just
$this.parent().parent().find("td.link input").get(0) === document.activeElement
However, that's not really what you want, you want an event handler instead, and you can add that during the creation of the element like this (with cleaned up code and cached elements etc)
$(function() {
$(document).on("click", "table.table-striped > tbody > tr > td > a.copy-btn", function(e) {
e.preventDefault();
var $this = $(this),
parent = $this.parent().parent(),
link = parent.find("td.link"),
text = link.text(),
input = $('<input />', {
'class' : 'form-control',
value : text,
type : 'text',
on : {
blur : function() {
$(this).parent().html(text);
}
}
});
link.html(input);
input.select().focus();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table table-striped">
<thead>
<tr>
<th class="title">link name</th>
<th class="title">Platform</th>
<th class="title">Link</th>
</tr>
</thead>
<tbody>
<tr>
<th class="body-text" scope="row">1</th>
<td class="body-text">Mark</td>
<td class="body-text link">Otto</td>
<td class="body-text">Copy
</td>
<td class="body-text">More
</td>
</tr>
<tr>
<th class="body-text" scope="row">1</th>
<td class="body-text">Otto</td>
<td class="body-text link">Mark</td>
<td class="body-text">Copy
</td>
<td class="body-text">More
</td>
</tr>
</tbody>
</table>
Change
if ($this.parent().parent().find("td.link input").blur()) {
$this.parent().parent().find("td.link").html(text);
}
to
var $input = $this.parent().parent().find("td.link input");
$input.off("blur")
$input.on("blur", function () {
$this.parent().parent().find("td.link").html(text);
});
Is something like this what you had in mind?
$(function() {
$(document).on("click", "table.table-striped > tbody > tr > td > a.copy-btn", function(e) {
e.preventDefault();
const $this = $(this),
text = $this.parent().parent().find("td.link").text();
$this.parent().parent().find("td.link").html('<input type="text" class="form-control" value="' + $.trim(text) + '">');
$this.parent().parent().find("td.link input").select();
$($this).closest('tr').find('.form-control').on('blur', function () {
var xThis = this;
var finText = $(xThis).val();
$(xThis).closest('td').html(finText);
$(xThis).off('blur');
});
return false;
});
})();
See my jsfiddle example: https://jsfiddle.net/fictus/mmwc06gm/

Sort only few rows of table - jquery/ javascript

I want to sort only child tr's data and don't want to move parent tr. Only child tr's will move until next parent. I have a table like this :
<table>
<tr>
<th id="column1">Column 1</th>
<th id="column2">Column 2</th>
<th>Column 3</th>
</tr>
<tr class="parent">
<td>text</td>
<td>text</td>
<td>text</td>
</tr>
<tr class="child">
<td>96</td>
<td>102</td>
<td>121</td>
</tr>
<tr class="child">
<td>455</td>
<td>422</td>
<td>410</td>
</tr>
<tr class="child">
<td>212</td>
<td>430</td>
<td>203</td>
</tr>
<tr class="parent">
<td>text</td>
<td>text</td>
<td>text</td>
</tr>
<tr class="child">
<td>363</td>
<td>581</td>
<td>231</td>
</tr>
<tr class="child">
<td>632</td>
<td>115</td>
<td>212</td>
</tr>
</table>
Javascript code :
$('#column1, #column2')
.each(function(){
var th = $(this),
thIndex = th.index(),
inverse = false;
th.click(function() {
// sorting classes don't work here b/c this function gets called repeatedly - moved to afterRequest: function
table.find('tr.parent td').filter(function(){
return $(this).index() === thIndex;
}).sortElements(function(a, b){
return $.text([a]) > $.text([b]) ?
inverse ? 1 : -1
: inverse ? -1 : 1;
}, function(){
// parentNode is the element we want to move
return this.parentNode;
// this.parentNode
});
inverse = !inverse;
});
});
fiddle : demo
Wrap each "section" in its own <tbody />
<tbody>
<tr class="parent"><!-- ... --></tr>
<tr class="child"><!-- ... --></tr>
<tr class="child"><!-- ... --></tr>
<tr class="child"><!-- ... --></tr>
</tbody>
<tbody>
<tr class="parent"><!-- ... --></tr>
<tr class="child"><!-- ... --></tr>
<tr class="child"><!-- ... --></tr>
</tbody>
<!-- ... -->
And do the sorting on each of the <tbody />'s
$("tbody").each(function() {
$(this).find('tr:not(.parent) td') // ignore the "parent" row
.filter(function () {
return $(this).index() === thIndex;
}).sortElements(function (a, b) {
return $(a).text() > $(b).text() ? inverse ? -1 : 1 : inverse ? 1 : -1;
}, function () {
return this.parentNode;
});
});
Example

Categories