jquery .each loop odd behaviour - javascript

Hi I have the following code
html
<table id="tbPermission">
<tr>
<th>User ID</th>
<th>User Name</th>
</tr>
<tr>
<td>1</td>
<td>Test1</td>
</tr>
<tr>
<td>2</td>
<td>Test2</td>
</tr>
<tr>
<td>3</td>
<td>Test3</td>
</tr>
</table>
script
var trArray = [];
var tdArray = [];
var reruiredObj = {"UserID":null,
"UserName":null
};
var first;
var second;
$('#tbPermission tr').each(function () {
$(this).find('td').each(function (index) {
//alert(index+'-'+ $(this).html());
//alert(index);
if(index == 0){
first = $(this).html();
}
else{
second = $(this).html();
}
//alert(JSON.stringify(reruiredObj));
});
alert(first+'-'+second)
reruiredObj['UserID'] = first;
reruiredObj['UserName'] = second;
trArray.push(reruiredObj);
});
alert(JSON.stringify(trArray));
Demo Here
My question why first and second in undefined in first alert, and why it is
[{"UserID":"3","UserName":"Test3"},{"UserID":"3","UserName":"Test3"},{"UserID":"3","UserName":"Test3"},{"UserID":"3","UserName":"Test3"}]
my desired output is
[{"UserID":"1","UserName":"Test1"},{"UserID":"2","UserName":"Test2"},{"UserID":"3","UserName":"Test3"}]

The scope of your reruiredObj object is incorrect which is why you get the same object three times. Try this instead:
var trArray = [];
var tdArray = [];
var first;
var second;
$('#tbPermission tr:gt(0)').each(function () {
var reruiredObj = {
"UserID": null,
"UserName": null
};
first = $(this).find('td').eq(0).html();
second = $(this).find('td').eq(1).html();
reruiredObj['UserID'] = first;
reruiredObj['UserName'] = second;
trArray.push(reruiredObj);
});
console.log(JSON.stringify(trArray));
jsFiddle example
And the undefined values come from iterating over the first row which you don't want, and can ignore with tr:gt(0)

The first alert gives undefined because the first row of the table does not contain any td element.
To exclude the first row from the loop:
$('#tbPermission tr').each(function (i) {
if (i != 0) {
// execute ..
}
});
As for the array, try this in each loop:
var reruiredObj = { "UserID": first , "UserName":second };
Check the DEMO

Below works fine for me.
Since your first tr doesnt have td it gives undefined error. Try below one
<table id="tbPermission">
<thead>
<tr>
<th>User ID</th>
<th>User Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Test1</td>
</tr>
<tr>
<td>2</td>
<td>Test2</td>
</tr>
<tr>
<td>3</td>
<td>Test3</td>
</tr>
</tbody>
</table>
<script>
$(function () {
var trArray = [];
var tdArray = [];
var reruiredObj = {"UserID":null,
"UserName":null
};
jsonObj = [];
var first;
var second;
$('#tbPermission tbody tr').each(function () {
$(this).find('td').each(function (index) {
//alert(index+'-'+ $(this).html());
//alert(index);
if(index == 0){
first = $(this).html();
}
else{
second = $(this).html();
}
//alert(JSON.stringify(reruiredObj));
});
alert(first+'-'+second)
item = {}
item ["UserID"] = first;
item ["UserName"] = second;
jsonObj.push(item);
});
console.log(jsonObj);
});
</script>
alert jsonObj. This gives the required result.

Related

Select all thead and tr has td except the last column

i make this code to get all the value of the table and to insert in array with index name.
codes work well, but i want to exclude the last column of the table.
var array = [];
var headers = [];
$('#idTable th').each(function(index, item)
{
headers[index] = $(item).html();
});
$('#idTable tr').has('td').each(function()
{
var arrayItem = {};
$('td', $(this)).each(function(index, item)
{
arrayItem[headers[index]] = $(item).html();
});
array.push(arrayItem);
});
i already tried not(:last-child) but the last column still included.
check my jsfiddle.
https://jsfiddle.net/del17/1yx3csrw/3/
This should do what you want:
var array = [];
var headers = [];
$('#idTable th:not(:last-child)').each(function() {
headers.push($(this).text())
})
$('#idTable tbody tr').each(function() {
var arrayItem = {};
$(this).find('td:not(:last-child)').each(function(i) {
arrayItem[headers[i]] = $(this).text();
})
array.push(arrayItem);
});
console.log(array)
console.log(headers)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="idTable">
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
</tr>
</thead>
<tbody>
<tr>
<td>a1</td>
<td>b1</td>
<td>c1</td>
<td>d1</td>
</tr>
<tr>
<td>a2</td>
<td>b2</td>
<td>c2</td>
<td>d2</td>
</tr>
</tbody>
</table>
I think for what you want to do it's better to use "text" instead of "html".
Also, don't use index too much if you know you iterate through the same order.
It's better to use "push" if you want to add elements to an array like you seem to do here.
here is fiddle : https://jsfiddle.net/p0qu5mc9/
var array = [];
var headers = [];
var columnlength = document.getElementById('idTable').rows[0].cells.length;
$('#idTable th').each(function(index, item)
{
if(columnlength-1 > index){
headers[index] = $(item).html();
}
});
$('#idTable tr').has('td').each(function()
{
var arrayItem = {};
$('td', $(this)).each(function(index, item)
{
if(columnlength-1 > index){
arrayItem[headers[index]] = $(item).html();
}
});
array.push(arrayItem);
});
alert(JSON.stringify(array));

Wrong array method sort() logic

I Cannot understand the array method sort() logic. I had to write an eventListener for the two elements Age and Letter. By clicking on them we can sort our table by age and letter.
All works fine, but I see something strange in the sort() logic. By clicking on the Letter - table must sort by alphabet for elements in the column Letter. By clicking on the Age - table must sort by digits order for elements in the column Age. But it does not sort right.
tbody = document.getElementById('grid');
function tableSort(event) {
var target = event.target;
var action = target.getAttribute('data-type');
var arr = [].slice.call(grid.rows, 1);
var self = this;
this.number = function() {
arr.sort(function(a, b) { // sort by digits in the column "Age"
a.cells[0].innerHTML;
b.cells[0].innerHTML;
return a - b;
});
grid.appendChild(...arr);
}
this.string = function() {
arr.sort(function(a, b) { // sort by words in the column "Letter"
a.cells[1].innerHTML;
b.cells[1].innerHTML;
return a > b;
});
grid.appendChild(...arr);
}
if (action) {
self[action]();
}
}
tbody.addEventListener('click', tableSort);
th {
cursor: pointer;
}
<table id="grid">
<thead>
<tr>
<th data-type="number">Age</th>
<th data-type="string">Letter</th>
</tr>
</thead>
<tbody>
<tr>
<td>5</td>
<td>BBBBB</td>
</tr>
<tr>
<td>12</td>
<td>AAAAA</td>
</tr>
<tr>
<td>1</td>
<td>DDDDD</td>
</tr>
<tr>
<td>9</td>
<td>CCCCC</td>
</tr>
<tr>
<td>2</td>
<td>KKKKK</td>
</tr>
</tbody>
</table>
<script>
</script>
Modified your code and got it working. Here if you need it:
function tableSort(event) {
var target = event.target;
var action = target.getAttribute("data-type");
var arr = [].slice.call(grid.rows, 1);
var self = this;
this.number = function() {
arr.sort(function(a, b) {
// sort by digits in the column "Age"
return Number(a.cells[0].innerHTML) - Number(b.cells[0].innerHTML);
});
arr.forEach(function(item, index) {
grid.appendChild(item);
});
};
this.string = function() {
arr.sort(function(a, b) {
// sort by words in the column "Letter"
var str1 = a.cells[1].innerHTML;
var str2 = b.cells[1].innerHTML;
return str1.localeCompare(str2);
});
arr.forEach(function(item, index) {
grid.appendChild(item);
});
};
if (action) {
self[action]();
}
}
tbody.addEventListener("click", tableSort);
How about this stackoverflow post Sorting HTML table with JavaScript for clarification and the original external article in which I found it with a full example?
Sorting tables with VanillaJS or JQuery
Example:
/**
* Modified and more readable version of the answer by Paul S. to sort a table with ASC and DESC order
* with the <thead> and <tbody> structure easily.
*
* https://stackoverflow.com/a/14268260/4241030
*/
var TableSorter = {
makeSortable: function(table){
// Store context of this in the object
var _this = this;
var th = table.tHead, i;
th && (th = th.rows[0]) && (th = th.cells);
if (th){
i = th.length;
}else{
return; // if no `<thead>` then do nothing
}
// Loop through every <th> inside the header
while (--i >= 0) (function (i) {
var dir = 1;
// Append click listener to sort
th[i].addEventListener('click', function () {
_this._sort(table, i, (dir = 1 - dir));
});
}(i));
},
_sort: function (table, col, reverse) {
var tb = table.tBodies[0], // use `<tbody>` to ignore `<thead>` and `<tfoot>` rows
tr = Array.prototype.slice.call(tb.rows, 0), // put rows into array
i;
reverse = -((+reverse) || -1);
// Sort rows
tr = tr.sort(function (a, b) {
// `-1 *` if want opposite order
return reverse * (
// Using `.textContent.trim()` for test
a.cells[col].textContent.trim().localeCompare(
b.cells[col].textContent.trim()
)
);
});
for(i = 0; i < tr.length; ++i){
// Append rows in new order
tb.appendChild(tr[i]);
}
}
};
window.onload = function(){
TableSorter.makeSortable(document.getElementById("myTable"));
};
table thead th {
cursor: pointer;
}
<table id="myTable">
<thead>
<th data-type="string">Name</th>
<th data-type="number">Age</th>
</thead>
<tbody>
<tr>
<td>John</td>
<td>42</td>
</tr>
<tr>
<td>Laura</td>
<td>39</td>
</tr>
<tr>
<td>Fred</td>
<td>18</td>
</tr>
<tr>
<td>Bod</td>
<td>26</td>
</tr>
</tbody>
</table>

Sum column totals depending on text of another column using jQuery

I have this code that I use to calculate totals in specific columns based on the text in a different column. It works just fine, but I'm learning, so I would like to know if there is way to consolidate this code. As you can see I run a "each()" twice, once for each column. The first each check for "A" in the first column, then goes to the second column and adds the rows that meet the criteria. Similar on the second column, just that it looks for "B" and add columns 3. Is there a way to run the each function only once and check both column at the same time?
JS:
//Second Column
var total = 0;
$("#theTable tr:contains('A') td:nth-of-type(2)").each(function () {
var pending = parseInt($(this).text());
total += pending;
});
$("#theTable tfoot tr:last-of-type td:nth-of-type(2)").text(total);
//Third Column
var total2 = 0;
$("#theTable tr:contains('B') td:nth-of-type(3)").each(function () {
var pending2 = parseInt($(this).text());
total2 += pending2;
});
$("#theTable tfoot tr:last-of-type td:nth-of-type(3)").text(total2);
HTML:
<table id="theTable">
<thead>
<tr>
<th>MONTH</th>
<th>PENDING</th>
<th>DENIED</th>
</tr>
</thead>
<tbody></tbody>
<tr>
<td>A</td>
<td>2</td>
<td>4</td>
</tr>
<tr>
<td>B</td>
<td>1</td>
<td>3</td>
</tr>
<tr>
<td>A</td>
<td>3</td>
<td>2</td>
</tr>
<tr>
<td>C</td>
<td>3</td>
<td>2</td>
</tr>
<tr>
<td>A</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>B</td>
<td>4</td>
<td>2</td>
</tr>
<tfoot>
<tr>
<td>TOTALS:</td>
<td></td>
<td></td>
</tr>
</tfoot>
This may look simple for some of you, but again, I'm just learning some JS now.
Thanks!
You could try something like this:
var total = {A:{row:1,t:0},B:{row:2,t:0}};
$('#theTable tr').each(function() {
$row = $(this);
$.each(total, function(key, col) {
rowFil = $row.filter(':contains("' + key + '")');
col.t += (rowFil) ? +rowFil.find('td:eq(' + col.row + ')').text() : 0;
});
});
$("#theTable tfoot tr:last td:eq(1)").text(total.A.t);
$("#theTable tfoot tr:last td:eq(2)").text(total.B.t);
someThing of this sort might Help ...
var trs = $('#'+tblID).find('tr');
var total1 = 0;
var total2 = 0;
$.each(trs, function(k, v) {
if ($(v).text == "A"){
total1 += parseInt($(v).parent('tr').find('td:eq(2)').text());
}
if ($(v).text == "B"){
total2 += parseInt($(v).parent('tr').find('td:eq(3)').text())
}
});
Here is another approach - I've summed up all statistics for all possible values:
var totals = [];
$('#theTable tbody tr').each(function(e) {
var tds= $(this).find('td');
var index = $(tds[0]).text();
var pending = parseInt($(tds[1]).text(), 10);
var denied = parseInt($(tds[2]).text(), 10);
if (totals[index] == undefined)
totals[index] = { Pending: 0, Denied: 0 };
totals[index].Pending += pending;
totals[index].Denied += denied;
});
for (var key in totals)
$('#theTable tfoot').append('<tr><td>'+key+'</td><td>'+
totals[key].Pending+'</td><td>'+totals[key].Denied+'</td></tr>');
I've also updated markup a little, here is jsfiddle. The code may be not so pretty, but doing more stuff and can be refactored.
Creating a second table with the sums makes it easier to analyse the data.
SOLUTION
JS
//make a list of unique months
var months = [];
$('#theTable tr td:nth-of-type(1)').each(function(){
var month = $(this).text();
if(months.indexOf(month) < 0) months.push(month);
});
console.log('months', months);
//make a data structure with sums
var data = {};
var tr = $('#theTable tr');
$.each(months, function(){
var month = this;
data[month] = {
pending: 0,
denied: 0
};
tr.each(function(){
var ch = $(this).children();
var m = $(ch[0]).text();
var pending = $(ch[1]).text();
var denied = $(ch[2]).text();
if(m == month) {
data[month].pending += parseInt(pending);
data[month].denied += parseInt(denied);
}
});
});
console.log('data', data);
//make a table with the data
var table = $('<table>');
table.append($('<tr>'+
'<th>MONTH</th>'+
'<th>PENDING</th>'+
'<th>DENIED</th>'+
'</tr>'));
$.each(data, function(month){
table.append($('<tr>'+
'<td>'+month+'</td>'+
'<td>'+data[month].pending+'</td>'+
'<td>'+data[month].denied+'</td>'+
'</tr>'));
});
$('body').append(table);

Iterate over table cells, re-using rowspan values

I have a simple HTML table, which uses rowspans in some random columns. An example might look like
A | B |
---|---| C
D | |
---| E |---
F | | G
I'd like to iterate over the rows such that I see rows as A,B,C, D,E,C, then F,E,G.
I think I can probably cobble together something very convoluted using cell.index() to check for "missed" columns in later rows, but I'd like something a little more elegant...
without jquery:
function tableToMatrix(table) {
var M = [];
for (var i = 0; i < table.rows.length; i++) {
var tr = table.rows[i];
M[i] = [];
for (var j = 0, k = 0; j < M[0].length || k < tr.cells.length;) {
var c = (M[i-1]||[])[j];
// first check if there's a continuing cell above with rowSpan
if (c && c.parentNode.rowIndex + c.rowSpan > i) {
M[i].push(...Array.from({length: c.colSpan}, () => c))
j += c.colSpan;
} else if (tr.cells[k]) {
var td = tr.cells[k++];
M[i].push(...Array.from({length: td.colSpan}, () => td));
j += td.colSpan;
}
}
}
return M;
}
var M = tableToMatrix(document.querySelector('table'));
console.table(M.map(r => r.map(c => c.innerText)));
var pre = document.createElement('pre');
pre.innerText = M.map(row => row.map(c => c.innerText).join('\t')).join('\n');
document.body.append(pre);
td {
border: 1px solid rgba(0,0,0,.3);
}
<table>
<tr>
<td colspan=2>A</td>
<td rowspan=2>B</td>
</tr>
<tr>
<td>C</td>
<td rowspan=3>D</td>
</tr>
<tr>
<td rowspan=2>E</td>
<td rowspan=4>F</td>
</tr>
<tr></tr>
<tr>
<td rowspan=2 colspan=2>G</td>
</tr>
<tr></tr>
<tr>
<td rowspan=3 colspan=3>H</td>
</tr>
<tr></tr>
<tr></tr>
<tr>
<td colspan=3>I</td>
</tr>
</table>
Try this:
<table id="tbl">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td colspan="2" rowspan="2">A</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
Script:
var finalResult = '';
var totalTds = $('#tbl TR')[0].length;
var trArray = [];
var trArrayValue = [];
var trIndex = 1;
$('#tbl TR').each(function(){
var currentTr = $(this);
var tdIndex = 1;
trArray[trIndex] = [];
trArrayValue[trIndex] = [];
var tdActuallyTraversed = 0;
var colspanCount = 1;
$('#tbl TR').first().children().each(function(){
if(trIndex > 1 && trArray[trIndex - 1][tdIndex] > 1)
{
trArray[trIndex][tdIndex] = trArray[trIndex - 1][tdIndex] - 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex - 1][tdIndex];
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
else
{
if(colspanCount <= 1)
{
colspanCount = currentTr.children().eq(tdActuallyTraversed).attr('colspan') != undefined ? currentTr.children().eq(tdActuallyTraversed).attr('colspan') : 1;
}
if(colspanCount > 1 && tdIndex > 1)
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex][tdIndex - 1];
colspanCount--;
}
else
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).html();
tdActuallyTraversed++;
}
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
tdIndex++;
});
trIndex++;
});
alert(finalResult);
Fiddle
i am not sure about the performance, but it works well.
what I understood with your question is: You want to split the merged cell with same value and then iterate the table simply by row.
I've created a JSFiddle that will split the merged cells with the same value. Then you'll have a table that can be iterated simply by rows to get the desired output that you specified.
See it running here http://jsfiddle.net/9PZQj/3/
Here's the complete code:
<table id="tbl" border = "1">
<tr>
<td>A</td>
<td>B</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td>D</td>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
<br>
<div id="test"> </div>
Here's the jquery that is used to manipulate the table's data.
var tempTable = $('#tbl').clone(true);
var tableBody = $(tempTable).children();
$(tableBody).children().each(function(index , item){
var currentRow = item;
$(currentRow).children().each(function(index1, item1){
if($(item1).attr("rowspan"))
{
// copy the cell
var item2 = $(item1).clone(true);
// Remove rowspan
$(item1).removeAttr("rowspan");
$(item2).removeAttr("rowspan");
// last item's index in next row
var indexOfLastElement = $(currentRow).next().last().index();
if(indexOfLastElement <= index1)
{
$(currentRow).next().append(item2)
}
else
{
// intermediate cell insertion at right position
$(item2).insertBefore($(currentRow).next().children().eq(index1))
}
}
});
console.log(currentRow)
});
$('#test').append(tempTable);
You can use this Gist. It supports all the requirements by W3C, even "rowspan=0" (which seems to be only supported by Firefox).

Click table row and get value of all cells

I don't know JQuery, so I'm hoping there is a way to do this in pure Javascript.
I need to click on a table row and get the value of each cell in that row. Here is the format of my table:
<table class='list'>
<tr>
<th class='tech'>OCB</th>
<th class='area'>Area</th>
<th class='name'>Name</th>
<th class='cell'>Cell #</th>
<th class='nick'>Nickname</th>
</tr>
<tr onclick="somefunction()">
<td>275</td>
<td>Layton Installation</td>
<td>Benjamin Lloyd</td>
<td>(801) 123-456</td>
<td>Ben</td>
</tr>
</table>
Is there anyway short of putting a unique ID to each cell?
There is no need to add ids or add multiple event handlers to the table. One click event is all that is needed. Also you should use thead and tbody for your tables to separate the heading from the content.
var table = document.getElementsByTagName("table")[0];
var tbody = table.getElementsByTagName("tbody")[0];
tbody.onclick = function (e) {
e = e || window.event;
var data = [];
var target = e.srcElement || e.target;
while (target && target.nodeName !== "TR") {
target = target.parentNode;
}
if (target) {
var cells = target.getElementsByTagName("td");
for (var i = 0; i < cells.length; i++) {
data.push(cells[i].innerHTML);
}
}
alert(data);
};
<table class='list'>
<thead>
<tr>
<th class='tech'>OCB</th>
<th class='area'>Area</th>
<th class='name'>Name</th>
<th class='cell'>Cell #</th>
<th class='nick'>Nickname</th>
</tr>
</thead>
<tbody>
<tr>
<td>275</td>
<td>Layton Installation</td>
<td>Benjamin Lloyd</td>
<td>(801) 123-456</td>
<td>Ben</td>
</tr>
</tbody>
</table>
Example:
http://jsfiddle.net/ZpCWD/
Check this fiddle link
HTML:
<table id="rowCtr" class='list'>
<thead>
<tr>
<th class='tech'>OCB</th>
<th class='area'>Area</th>
<th class='name'>Name</th>
<th class='cell'>Cell #</th>
<th class='nick'>Nickname</th>
</tr>
</thead>
<tbody>
<tr>
<td>275</td>
<td>Layton Installation</td>
<td>Benjamin Lloyd</td>
<td>(801) 123-456</td>
<td>Ben</td>
</tr>
</tbody>
</table>
JAVASCRIPT:
init();
function init(){
addRowHandlers('rowCtr');
}
function addRowHandlers(tableId) {
if(document.getElementById(tableId)!=null){
var table = document.getElementById(tableId);
var rows = table.getElementsByTagName('tr');
var ocb = '';
var area = '';
var name = '';
var cell = '';
var nick = '';
for ( var i = 1; i < rows.length; i++) {
rows[i].i = i;
rows[i].onclick = function() {
ocb = table.rows[this.i].cells[0].innerHTML;
area = table.rows[this.i].cells[1].innerHTML;
name = table.rows[this.i].cells[2].innerHTML;
cell = table.rows[this.i].cells[3].innerHTML;
nick = table.rows[this.i].cells[4].innerHTML;
alert('ocb: '+ocb+' area: '+area+' name: '+name+' cell: '+cell+' nick: '+nick);
};
}
}
}
var elements = document.getElementsByTagName('td');
for (var i =0; i < elements.length; i++) {
var cell_id = 'id' + i;
elements[i].setAttribute('id', cell_id);
}
Maybe put something like this in function your onclick links to from the tr?
$("tr").click(function () {
var rowItems = $(this).children('td').map(function () {
return this.innerHTML;
}).toArray();
});
This shows the row's first cell which is clicked according to dataTr.querySelectorAll("td")[0].innerText;
document.querySelector("#myTable").addEventListener("click",event => {
let dataTr = event.target.parentNode;
let dataRes = dataTr.querySelectorAll("td")[0].innerText;
console.log(dataRes);
});

Categories

Resources