Sort checkbox(checked and unchecked) in table - javascript

I want to sort my checkbox when i click on the X:
JS to sort my checkbox(checked and unchecked)?
I got no idea how to write it. please help.
The following code is borrowed.
The Price and stock value will be pass from other JS file using router.
But for now I make it simple because I want to know how to sort the checkbox.
var sortedPrice = false;
function sortPrice() {
$('#myTable').append(
$('#myTable').find('tr.item').sort(function (a, b) {
var td_a = $($(a).find('td.sortPrice')[0]);
var td_b = $($(b).find('td.sortPrice')[0]);
if(sortedPrice){
if(td_a.html() == 'Free') return -1;
return td_b.html().replace(/\D/g, '') - td_a.html().replace(/\D/g, '');
}else{
if(td_a.html() == 'Free') return 1;
return td_a.html().replace(/\D/g, '') - td_b.html().replace(/\D/g, '');
}
})
);
if(sortedPrice) sortedPrice = false;
else sortedPrice = true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table" id="myTable">
<tr>
<th onclick="sortPrice()">Price</th>
<th>Stock</th>
<th>%</th>
<th>X</th>
</tr>
<tr class="item">
<td class="sortPrice">1</td>
<td>1</td>
<td>2</td>
<td><input type="checkbox" value="1"></td>
</tr>
<tr class="item">
<td class="sortPrice">4</td>
<td>3</td>
<td>1</td>
<td><input type="checkbox" value="2"></td>
</tr>
<tr class="item">
<td class="sortPrice">7</td>
<td>4</td>
<td>6</td>
<td><input type="checkbox" value="3"></td>
</tr>
<tr class="item">
<td class="sortPrice">2</td>
<td>7</td>
<td>8</td>
<td><input type="checkbox" value="4"></td>
</tr>
<tr class="item">
<td class="sortPrice">3</td>
<td>4</td>
<td>2</td>
<td><input type="checkbox" value="5"></td>
</tr>
</table>

I would try to make the click handler generic by taking the following steps:
Create a function that takes an array of pairs, and sorts that array by the first value in every pair, and returns the sorted array with just the second value from each pair in sorted order. This generic function can be used to pass pairs of cell-content and corresponding row element. This function could also take care of reversing the order when the input pairs were already sorted.
Create a single click handler for the td elements (the column headers). Let it collect the cells in the corresponding column, and for each cell determine whether the checkbox state should be taken as value, or the text content of that cell.
After sorting the values in the column with the first function, the rows can be fed into the table again.
Use the compare function from Intl.Collator so to have numeric sort when appropriate.
This way you can do away with some of the HTML (onclick, sortPrice, item, ...)
const {compare} = new Intl.Collator(undefined, {numeric: true});
function sortSecondByFirst(pairs) {
const sorted = [...pairs].sort(([a], [b]) => compare(a, b))
.map(([,a]) => a);
if (pairs.every(([,a], i) => a === sorted[i])) {
sorted.reverse(); // Was already sorted
}
return sorted;
}
$("th", "#myTable").click(function () {
sortColumn($(this).index());
});
function sortColumn(colIdx) {
const $cells = $(`tr > td:nth-child(${colIdx+1})`, "#myTable");
$("#myTable").append(
sortSecondByFirst($cells.get().map((cell) => {
const $input = $('input[type=checkbox]', cell);
const value = $input.length ? $input.prop("checked") : $(cell).text();
return [
value,
$(cell).parent()
];
}))
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="myTable">
<tr>
<th>Price</th><th>Stock</th><th>%</th><th>X</th>
</tr>
<tr>
<td>1</td><td>1</td><td>2</td>
<td><input type="checkbox" value="1"></td>
</tr>
<tr>
<td>4</td><td>3</td><td>1</td>
<td><input type="checkbox" value="2"></td>
</tr>
<tr>
<td>7</td><td>4</td><td>6</td>
<td><input type="checkbox" value="3"></td>
</tr>
<tr>
<td>20</td><td>7</td><td>8</td>
<td><input type="checkbox" value="4"></td>
</tr>
<tr>
<td>3</td><td>4</td><td>2</td>
<td><input type="checkbox" value="5"></td>
</tr>
</table>

Quite honestly if u have a choice I'd always go use Vue, react or the like as a ui framework. There this is simpler and u have a better -in my eyes - split of html template and data. Vue is quite easy to learn from my experience too.(great tutorials eg on YouTube)
That said in jQuery I guess I would write a sort function like the one u got there that via onclick event it triggered when X is clicked on and for the sorting write a similar compare function as above. Eg
(a,b) => a.checked - b.checked;
Hope this makes sense to you or where precisely do u struggle?

Related

sum of column using jquery [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I need to sum of column With OnKeyup or OnChange
Here's my code:
$(document).ready(function() {
$(".expenses").on('keyup change', calculateSum);
});
function calculateSum() {
var $input = $(this);
var $row = $input.closest('tr');
var sum = 0;
$row.find(".expenses").each(function() {
sum += parseFloat(this.value) || 0;
});
$row.find(".expenses_sum").val(sum.toFixed(2));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border="1">
<tr>
<th>sl</th>
<th>TA</th>
<th>DA</th>
</tr>
<tr>
<td>1</td>
<td><input class="expenses"></td>
<td><input class="expenses"></td>
</tr>
<tr>
<td>2</td>
<td><input class="expenses"></td>
<td><input class="expenses"></td>
</tr>
<tr>
<td>Total</td>
<td><input class="expenses_sum"></td>
<td><input class="expenses_sum"></td>
</tr>
</table>
This is when the "context" of the input that matters: you want to update the sum that is in the same column where the input element was updated.
What you can do is:
Get the index of the <td> element the input belongs to
Calculate the sum of all expenses belonging to the same column. This is done by filtering (using .filter()) all .expenses elements to ensure that their parent's <td> index matches that you've determined in step 2
Set the sum on the corresponding .expenses_sum element in the same column. This is again, done by filtering all .expenses_sum elements and only getting the one whose parent <td> index matches
Some additional pro-tips:
Listen to the onInput event. For input elements, that covers onKeyUp and onChange events, for convenience.
Use <input type="number" /> to prevent users from erroneously entering non-numerical characters
Use <input readonly /> on the .expenses_sum element, so that users don't fiddle with that sum by their own
Remember to cast the value of the input elements to a number. This can be done by using the + operator, i.e. +this.value. Remember that as per spec, all input elements, regardless their type, always has their value in type of string
Chain .each(calculateSum) to your original selection, so that you also compute the sum when the page is first loaded, i.e. $(".expenses").on('input', calculateSum).each(calculateSum);. This is very helpful when the .expenses elements might be pre-populated with values from the server-side (or if you have manually defined value="..."), for example.
See proof-of-concept below:
$(document).ready(function() {
$(".expenses").on('input', calculateSum).each(calculateSum);
});
function calculateSum() {
// Get the index of the parent `<td>` element
var cellIndex = $(this).closest('td').index();
// Get the values of expenses in the same column as the `<td>` element
var allExpensesInSameColumn = $('.expenses').map(function() {
if ($(this).closest('td').index() !== cellIndex)
return;
return +this.value;
}).get();
// Calculate the sum from returned array of values
var sumOfExpensesInSameColumn = allExpensesInSameColumn.reduce(function(acc, curVal) {
return acc + curVal;
});
// Set the sum on the `.expenses_sum` element in the corresponding column
$('.expenses_sum').each(function() {
if ($(this).closest('td').index() !== cellIndex)
return;
this.value = sumOfExpensesInSameColumn;
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border="1">
<tr>
<th>sl</th>
<th>TA</th>
<th>DA</th>
</tr>
<tr>
<td>1</td>
<td><input class="expenses" type="number" /></td>
<td><input class="expenses" type="number" /></td>
</tr>
<tr>
<td>2</td>
<td><input class="expenses" type="number" /></td>
<td><input class="expenses" type="number" /></td>
</tr>
<tr>
<td>Total</td>
<td><input class="expenses_sum" readonly></td>
<td><input class="expenses_sum" readonly></td>
</tr>
</table>

Sorting dynamically generated Table Alphabetically

I'm trying to sort a table by alphabetical order. This is based on the 2nd column of each row (under the header 'Title'). My code works great when isolated in an html file. Located here...
The problem occurs when I try to make it work with a dynamically built table. I'll show the important parts of the code below.
sortTable function...
function sortTable() {
var table, rows, switching, i, x, y, shouldSwitch;
table = document.getElementById("manga-tracker").getElementsByTagName('table')[0];
switching = true;
/* Make a loop that will continue until
no switching has been done: */
while (switching) {
// Start by saying: no switching is done:
switching = false;
rows = table.getElementsByTagName("tr");
/* Loop through all table rows (except the
first, which contains table headers): */
for (var i = 1; i < (rows.length - 1); i++) {
// Start by saying there should be no switching:
shouldSwitch = false;
/* Get the two elements you want to compare,
one from current row and one from the next: */
x = rows[i].getElementsByTagName("td")[1].getElementsByTagName('a')[0].text;
y = rows[i + 1].getElementsByTagName("td")[1].getElementsByTagName('a')[0].text;
console.log('x: '+x+', y: '+y);
// Check if the two rows should switch place:
if (x.toLowerCase() > y.toLowerCase()) {
// If so, mark as a switch and break the loop:
shouldSwitch = true;
break;
}
}
if (shouldSwitch) {
/* If a switch has been marked, make the switch
and mark that a switch has been done: */
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
}
}
}
This is what creates the table by receiving data from server. The HTML produced by this function looks just like the HTML I supplied in the codepen example at the top of this post.
function populateMangaTracker() {
$('#manga-tracker').html("<table><th> </th><th>Title</th><th>Released</th><th>Owned</th><th>Read</th><th>Last Updated</th><th>Posted By</th></table>");
$.ajax({
method: 'GET',
url: 'populate-mangatracker.php',
});
$.getJSON('populate-mangatracker.php', function(data){
$.each( data, function( key, val ) {
$('#manga-tracker table').append("<tr><td><input type='checkbox' name='delete-manga' value='"+val.id+"' /></td><td><a href='?id="+val.id+"'>"+val.title+"</a></td><td>"+val.total+"</td><td>"+val.owned+"</td><td>"+val.volRead+"</td><td>"+val.lastUpdated+"</td><td>"+ val.owner.charAt(0).toUpperCase() + val.owner.substr(1) +"</td></tr>");
});
// add onclick event to #manga-tracker links
$('#manga-tracker a').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var pathName = $(this).attr('href').split('=');
pathName = pathName[1];
if(pathName >= 0) {
window.location = 'edit-manga.php?id='+pathName;
}
});
});
}
Inside of the tableSort() function... When I try to console.log() inside of the for() loop it doesn't work because the variable 'rows' only has a length of 1 therefore anything past index 1 is unknown.
When I console.log() 'rows' before the for() loop, it contains all 10 of the tr tags. I don't understand where the contents of the array are going... Can someone help me get this working?
Please and thank you!
If you're OK with removing the table elements and adding them again, try this:
const tbody = document.querySelector('#manga-tracker tbody');
const [trhead, ...trs] = [...tbody.children];
trs.sort((tr1, tr2) => tr1.children[1].textContent.localeCompare(tr2.children[1].textContent));
tbody.textContent = '';
[trhead, ...trs].forEach(tr => tbody.appendChild(tr));
<div id="manga-tracker-wrapper">
<form action="delete-manga.php" method="POST">
<div id="manga-tracker">
<table>
<tbody>
<tr>
<th> </th>
<th>Title</th>
<th>Released</th>
<th>Owned</th>
<th>Read</th>
<th>Last Updated</th>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="7"></td>
<td>Arifureta: From Commonplace to World's Strongest</td>
<td>20</td>
<td>1</td>
<td>1</td>
<td>2018-05-14</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="6"></td>
<td>Akame ga KILL!</td>
<td>14</td>
<td>14</td>
<td>14</td>
<td>2018-05-13</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="11"></td>
<td>Dragonball Super</td>
<td>2</td>
<td>1</td>
<td>1</td>
<td>2018-05-14</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="12"></td>
<td>7th Garden</td>
<td>8</td>
<td>1</td>
<td>1</td>
<td>2018-05-14</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="13"></td>
<td>Attack on Titan</td>
<td>25</td>
<td>2</td>
<td>0</td>
<td>2018-05-14</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="14"></td>
<td>The Ancient Magus' Bride</td>
<td>8</td>
<td>7</td>
<td>7</td>
<td>2018-05-14</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="15"></td>
<td>Beasts of Abigaile</td>
<td>3</td>
<td>3</td>
<td>3</td>
<td>2018-05-14</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="16"></td>
<td>Berserk</td>
<td>39</td>
<td>6</td>
<td>6</td>
<td>2018-05-14</td>
</tr>
<tr>
<td><input type="checkbox" name="delete-manga" value="17"></td>
<td>Fairy Tale</td>
<td>63</td>
<td>1</td>
<td>0</td>
<td>2018-05-14</td>
</tr>
</tbody>
</table>
</div>
</form>
<div id="delete-response">
</div>
</div>
I tried #certainperformances solution. Although it would work when isolated in a separate file, it wouldn't work in my project. Then I thought about what #certainperformances suggested in terms of just storing the data and not the elements.It hit me. I could just sort the data before I output it to the table. I was just complicating things.
function populateMangaTracker() {
var toBeSorted = [];
$('#manga-tracker').html("<table><th> </th><th>Title</th><th>Released</th><th>Owned</th><th>Read</th><th>Last Updated</th><th>Posted By</th></table>");
$.ajax({
method: 'GET',
url: 'populate-mangatracker.php',
});
$.getJSON('populate-mangatracker.php', function(data){
$.each( data, function( key, val ) {
/* Stored data in an array */
toBeSorted[key] = {
'id': val.id,
'title': val.title,
'total': val.total,
'owned': val.owned,
'read': val.volRead,
'lastUpdated': val.lastUpdated,
'owned': val.owner.charAt(0).toUpperCase() + val.owner.substr(1)
};
});
/* Then sorted alphabetically here */
toBeSorted.sort(sortObject); // sort alphabetically
/* And outputted data to table here */
for(var i = 0; i < toBeSorted.length; i++) {
$('#manga-tracker table').append("<tr><td><input type='checkbox' name='delete-manga' value='"+toBeSorted[i].id+"' /></td><td><a href='?id="+toBeSorted[i].id+"'>"+toBeSorted[i].title+"</a></td><td>"+toBeSorted[i].total+"</td><td>"+toBeSorted[i].owned+"</td><td>"+toBeSorted[i].volRead+"</td><td>"+toBeSorted[i].lastUpdated+"</td><td>"+toBeSorted[i].owner+"</td></tr>");
}
});
}

How to retrieve references from group Checkbox name arrays using JS or jQuery

I have a hard JS/jQuery riddle ! Hard because I couldn't find it on Google nor here, neither now, nor months ago when I was looking for it previously.
A large framework is using checkboxes in a table:
<table class="ListTable">
<tr>
<td><input name="blnChecked[70_20]" type="checkbox" value="1" id="some_unusable_gobbledy_gook" /></td>
<td></td>...
</tr>
<tr>
<td><input name="blnChecked[71_20]" type="checkbox" value="1" id="some_more_unusable_gobbledy_gook" /></td>
<td></td>...
</tr>
<tr>
<td><input name="blnChecked[70_25]" type="checkbox" value="1" id="some_further_unusable_gobbledy_gook" /></td>
<td></td>...
</tr>
</table>
I now need to collect all checkbox name references into an array: 70_20, 71_20 and 70_25 in the above example. Then join them up, and submit them as a URL parameter to a different page (although this joining is not essential to my question).
Question: Using JS/jQuery on the same page, how do I get these references from the name strings in these (checked) checkboxes in an array ?
I prefer not to use regexes (a bit messy, or 'overkill' for such a seeming trivial matter imho), although such a solution is not off my table.
(If someone asks why the table is structured as such: This is not my doing. But I can see that when such a form, in which this table is submitted to a PHP page, the PHP stores all such checkboxes into a single array, which is very nice, and I wanted to achieve a similar effect with JS/jQuery.)
A way to create on client side the array is based on using:
.map()
string .replace()
$('#btn').on('click', function(e) {
var retVal = $('table.ListTable :checkbox[name^="blnChecked["]:checked').map(function(idx, ele) {
//
// if the name value has always the same format...
//
return ele.name.replace('blnChecked[', '').replace(']', '');
//
// or....
//
// return ele.name.split('[').pop().replace(']', '');
// return ele.name.substr(11, 5);
//return ele.name.replace(/blnChecked\[(.*?)\]/g, '$1')
}).get();
var param = $.param({'param': retVal.join(',')});
console.log('Array: ' + retVal);
console.log('URL param: ' + param);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="ListTable">
<tr>
<td><input name="blnChecked[7125_2355]" type="checkbox" value="1" id="some_unusable_gobbledy_gook" /></td>
<td></td>
</tr>
<tr>
<td><input name="blnChecked[71_20]" type="checkbox" value="1" id="some_more_unusable_gobbledy_gook" /></td>
<td></td>
</tr>
<tr>
<td><input name="blnChecked[70_25]" type="checkbox" value="1" id="some_further_unusable_gobbledy_gook" /></td>
<td></td>
</tr>
</table>
<button type="button" id="btn">Click Me</button>

JavaScript onBlur for multiple fields

I have a table that consists of many text input fields which the user can assign values to. My goal is that if the user "onBlur"s any of the fields then a function will activate. I could resolve the issue by marking each cell individually, however it would be very repetitive and i'm sure there's a more efficient way around this.
To demonstrate:
<table>
<tr>
<td>I</td>
<td><input type="text" id="whatever1"></td>
</tr>
<tr>
<td>Love</td>
<td><input type="text" id="whatever2"></td>
</tr>
<tr>
<td>Stack Overflow</td>
<td><input type="text" id="whatever3"></td>
</tr>
</table>
With JS:
var e1 = document.getElementById('whatever1');
e1.onblur = alias;
function alias() {
alert('started');
}
and then repeat this for each input box another 2 times. Or hopefully there's an easier way.
You can delegate the event and put a listener on a containing element:
var e1 = document.getElementById('containing-table');
e1.addEventListener('blur', function(e){
alert(e.target);
}, true);
and the modified html:
<table id="containing-table">
<tr>
<td>I</td>
<td><input type="text" id="whatever1"></td>
</tr>
<tr>
<td>Love</td>
<td><input type="text" id="whatever2"></td>
</tr>
<tr>
<td>Stack Overflow</td>
<td><input type="text" id="whatever3"></td>
</tr>
</table>
here's a fiddle: http://jsfiddle.net/oj2wj1d6/7/
The advantage of this is that you can actually remove and add input elements and the listener will capture events on new nodes. You can add conditional statements inside of the function in addEventListener in order to further filter how you would want to respond to different types of event targets.
with jQuery, you could do something as simple as:
$("table").on("blur", "input", function(e){
alert(e.target);
});
Some useful documentation to learn more:
The blur event, scroll down for details about event delegation.
addEventListener.
more about doing event delegation in vanilla JS
<table>
<tr>
<td>I</td>
<td><input class="blurMe" type="text" id="whatever1"></td>
</tr>
<tr>
<td>Love</td>
<td><input class="blurMe" type="text" id="whatever2"></td>
</tr>
<tr>
<td>Stack Overflow</td>
<td><input class="blurMe" type="text" id="whatever3"></td>
</tr>
</table>
Then in javascript
//inputs as NodeList
var inputs = document.querySelectorAll(".blurMe");
//Convertion to Array
var inputsArr = Array.prototype.slice.call(input);
// Loop to asign event
inputsArr.forEach(function(item){
item.onBlur = alias;
});
Add a common class to all your element and use this for select all element getElementByClassname. if you want see exact what if your curent element add parameter event your function. and e.target give you DOM element.
how about this ?
<script>
document.getElementById()
var arr = document.getElementsByClassName('whatever');
for(var i=0;i<arr.length;i++)
{
arr[i].onblur=alias;
}
function alias() {
alert('started');
}
</script>
<table>
<tr>
<td>I</td>
<td><input type="text" class="whatever"></td>
</tr>
<tr>
<td>Love</td>
<td><input type="text" class="whatever"></td>
</tr>
<tr>
<td>Stack Overflow</td>
<td><input type="text" class="whatever"></td>
</tr>
</table>

How to extract text inside nested HTML using JQuery?

I have here HTML Code:
<div class="actResult" style="border: solid">
<table>
<tbody>
<tr>
<td>Order Number</td>
<td>1</td>
</tr>
<tr>
<td>Customer Number</td>
<td>3</td>
</tr>
<tr>
<td>Complaint Code</td>
<td>b</td>
</tr>
<tr>
<td>Receivable Receipt Number</td>
<td>5</td>
</tr>
<tr>
<td>Date Called</td>
<td>2014-03-19</td>
</tr>
<tr>
<td>Scheduled Day Of Checkup</td>
<td>2014-03-19</td>
</tr>
<tr>
<td>Scheduled Day Of Service</td>
<td>2014-03-21</td>
</tr>
<tr>
<td>Checkup Status</td>
<td>Y</td>
</tr>
<tr>
<td>Service Status</td>
<td>N</td>
</tr>
<tr>
<td>Technician Number Checkup</td>
<td>3</td>
</tr>
<tr>
<td>Technician Number Service</td>
<td>1</td>
</tr>
</tbody>
</table>
</div>
I want to get the values of the tags and put them into an array with the a structure like array("first td" => "second td"), so for this case the array would be array("Order Number" => "1", "Customer Number" => "3", "Complaint Code" => "b", ...) and so on.
After that, the final array would be sent into a PHP code.
I've been trying to extract some of the values from the HTML using var html = $(this).filter(function( index ){ return $("td", this) }).filter(":odd").text(); and various other combinations of filter(), but it doesn't seem to work for me.
How do I go about doing what I want to do?
jsFiddle Demo
You are going to want to use .each for that and iterate through the rows in the table. For each row, take the first cell (.eq(0)) as the key, and the second cell (.eq(1)) as the value. Place these in a result object.
//object to hold resulting data
var result = {};
//iterate through rows
$('.actResult tr').each(function(){
//get collection of cells
var $tds = $(this).find('td');
//set the key in result to the first cell, and the value to the second cell
result[$tds.eq(0).html()] = $tds.eq(1).text();
});
You can get the rows property of the table element and create an object based on the cells' value:
var rows = document.querySelector('.actResult table').rows,
data = {}, c, l = rows.length, i = 0;
for (; i < l; i++) {
c = rows[i].cells;
data[c[0].innerHTML] = c[1].innerHTML;
}
http://jsfiddle.net/tG8F6/

Categories