I have an html table that I want to read from and create a new table underneath it from reading the first table. The first table looks like this:
ID | Value
100 | 3
200 | 2
400 | 7
100 | 4
and should output this
ID | Total
100 | 7
200 | 2
400 | 7
I'm having trouble creating the new rows after the first row and adding them based on ID, heres what I have so far
var id = document.getElementByID("total");
var td = document.createElement('td');
var eleName = document.getElementsByName('initValue');
var total = 0;
for (var i = 1; i < eleName.length; i++) {
total += parseInt(eleName[i].value);
}
td.textContent = total;
id.appendChild(td);
Right now its just adding all the values
The ID can only increase by 100 and can have more than just 100-400 and more entries. The inital table is made with php
original table html
<table>
<tr><th>ID</th><th>Value</th></tr>
<tr><td name="itin" id="itin">100</td><td id="initValue" name="initValue">3</td></tr>
<tr><td name="itin" id="itin">200</td><td id="initValue" name="initValue">2</td></tr>
<tr><td name="itin" id="itin">400</td><td id="initValue"name="initValue">7</td></tr>
<tr><td name="itin" id="itin">100</td><td id="initValue" name="initValue">4</td></tr>
</table>
As a few people have said in the comments an element's ID, <el id="something">, must be unique and there cannot be any duplicates of it on the page. If you want to "group" similar elements use a class.
For solving your problem, since the value of your ID is is a direct sibling we only need one selector to get the ID and Value:
const itin = document.querySelectorAll('[name="itin"]');
With this we can loop over every ID element, name="itin", and get the value with el.nextElementSibling.textContent. We're going to be keeping track of our IDs and Values in an object since javascript doesn't have key/value pair arrays: let values = {}.
We use .nextElementSibling to ignore white spaces and only get the next element.
We check if values already has a record of our ID with hasOwnProperty, if it does, we add the values together, if not we create a property in values with our ID and give it a value:
if (values.hasOwnProperty(inner)) {
values[inner] = values[inner] += parseInt(next);
} else {
values[inner] = parseInt(next);
}
Next we create a second loop to iterate over all properties in values and build our new table with that and the rest is pretty straight forward.
The two loops could likely be combined into one with a bit more logic to search for matching IDs.
const itin = document.querySelectorAll('[name="itin"]');
let values = {};
itin.forEach(item => {
const inner = item.textContent;
let next = null;
/* For direct sibling use this */
//const next = item.nextElementSibling.textContent;
/* For an unknown sibling use this */
for ( let a = 0; a < item.parentElement.children.length; a++ ) {
const n = item.parentElement.children[a];
if ( n.getAttribute('name') === 'initValue') {
next = n;
}
}
next = next.textContent;
/****/
if (values.hasOwnProperty(inner)) {
values[inner] = values[inner] += parseInt(next);
} else {
values[inner] = parseInt(next);
}
});
const table_two = document.querySelector('.table-two tbody');
for (let prop in values) {
const val = values[prop];
let tr = document.createElement('tr');
let td1 = document.createElement('td');
let td2 = document.createElement('td');
td1.innerHTML = prop;
td2.innerHTML = val;
tr.appendChild(td1);
tr.appendChild(td2);
table_two.appendChild(tr);
}
<table>
<tr>
<th>ID</th>
<th>Value</th>
</tr>
<tr>
<td name="itin">100</td>
<td name="initValue">3</td>
</tr>
<tr>
<td name="itin">200</td>
<td name="initValue">2</td>
</tr>
<tr>
<td name="itin">400</td>
<td name="initValue">7</td>
</tr>
<tr>
<td name="itin">100</td>
<td name="initValue">4</td>
</tr>
</table>
<table class="table-two">
<thead>
<tr>
<th>ID</th>
<th>Value</th>
</tr>
</thead>
<tbody></tbody>
</table>
An entirely javascript solution based on what you have provided is available on this jsfiddle
var tds = document.getElementsByName("itin");
var tdDict = {};
var keys = [];
for(var i=0;i<tds.length;i++){
var tdId = tds[i];
var tdVal = tds[i].nextSibling;
if(tdId.textContent in tdDict){
var curTotal = tdDict[tdId.textContent];
var newTotal = curTotal + parseInt(tdVal.textContent);
tdDict[tdId.textContent] = newTotal;
}
else{
tdDict[tdId.textContent] = parseInt(tdVal.textContent);
keys.push(tdId.textContent);
}
}
var totalDiv = document.getElementById("totals");
var totalTable = document.createElement("table");
totalDiv.append(totalTable);
var hrow = document.createElement("tr");
var idHeader = document.createElement("th");
idHeader.textContent = "ID";
var totalHeader = document.createElement("th");
totalHeader.textContent = "Total";
totalTable.append(hrow);
hrow.append(idHeader);
hrow.append(totalHeader);
for(var i=0;i<keys.length; i++){
var newRow = document.createElement("tr");
var idVal = keys[i];
var valVal = tdDict[idVal];
var idValTd = document.createElement("td");
idValTd.textContent = idVal;
var valValTd = document.createElement("td");
valValTd.textContent = valVal;
newRow.appendChild(idValTd);
newRow.appendChild(valValTd);
totalTable.appendChild(newRow);
}
<table>
<tr><th>ID</th><th>Value</th></tr>
<tr><td name="itin" id="itin">100</td><td id="initValue" name="initValue">3</td></tr>
<tr><td name="itin" id="itin">200</td><td id="initValue" name="initValue">2</td></tr>
<tr><td name="itin" id="itin">400</td><td id="initValue"name="initValue">7</td></tr>
<tr><td name="itin" id="itin">100</td><td id="initValue" name="initValue">4</td></tr>
</table>
<div id="totals">
</div>
Related
I'm trying to append items from a 2D array into a table, but everything is appearing in the one column on the left. How can I separate tableDataArr[1] to start loading into the next column across?
Javascript
let names = []; //populated dynamically
let language = []; //populated dynamically
let tableDataArr = [names, language];
function renderData() {
for (let i = 0; i < tableDataArr.length; i++) {
tableDataArr[i].forEach(j => {
let newRow = document.createElement("tr");
newRow.className = "row";
newRow.innerHTML = `<td class='cell'>${j}</td>`;
leftTable.appendChild(newRow);
});
}
}
HTML
<div class='left-tbl-wrap'>
<table class='table' id='left-table'>
<tr class='row'>
<th class='th'>Name</th>
<th class='th'>Language</th>
<th class='th'>Latest tag</th>
<th class='th'><span class='delete'></span></th>
</tr>
</table>
</div>
For each iteration add name and language to the same row, then insert that row into table.
I added some elements in names and languages array to demostrate
let names = ["name1", "name2", "name3"]; //populated dynamically
let language = ["language1", "language2", "language3"]; //populated dynamically
let tableDataArr = [names, language];
const leftTable = document.querySelector("#left-table");
function renderData() {
tableDataArr[0].forEach((j, i) => {
let newRow = document.createElement("tr");
newRow.className = "row";
newRow.innerHTML = `<td class='cell'>${j}</td><td class='cell'>${tableDataArr[1][i]}</td>`;
leftTable.appendChild(newRow);
});
}
renderData();
<div class='left-tbl-wrap'>
<table class='table' id='left-table'>
<tr class='row'>
<th class='th'>Name</th>
<th class='th'>Language</th>
<th class='th'>Latest tag</th>
<th class='th'><span class='delete'></span></th>
</tr>
</table>
</div>
It should be like that
function renderData() {
for (let i = 0; i < tableDataArr.length; i++) {
let newRow = document.createElement("tr");
tableDataArr[i].forEach(j=> {
newRow.className = "row";
newRow.innerHTML = `<td class='cell'>${j}</td>`;
leftTable.appendChild(newRow);
});
}
}
Because you may do not need to iterate table row in the second loop. If you do like that it will be add each row with only one table data.
I have a table where each row has its unique id. Say there are rows with id='id25' and id='id26'. I need to insert a new row after row with id='id25'. I am using Vanilla JS without jQuery.
I have tried this:
var refElement = document.getElementById('id'+id);
var newrow = document.createElement("tr");
if (refElement) {
refElement.insertBefore(newrow, refElement.nextSibling);
}
but it throws me an error saying
Failed to execute 'insertBefore' on 'Node'
The node before which the new node is to be inserted is not a child of this node.
I know how to insert rows into top or bottom of the table but now I have an id as a reference to a particular row.
Any suggestions would be welcome
You want to insert into the refElement's parent, not refElement itself:
refElement.parentNode.insertBefore(newrow, refElement.nextSibling);
// -------^^^^^^^^^^^
var id = 1;
var refElement = document.getElementById('id' + id);
var newrow = document.createElement("tr");
newrow.innerHTML = "<td>new row</td>";
if (refElement) {
refElement.parentNode.insertBefore(newrow, refElement.nextSibling);
}
<table>
<tbody>
<tr id="id1"><td>refElement</td></tr>
<tr><td>original next sibling</td></tr>
</tbody>
</table>
(And yes, for anyone wondering, it'll work even if the refElement is the last row in the table.)
Inserting five rows per comment:
var id = 1;
var refElement = document.getElementById('id' + id);
var n, newrow;
if (refElement) {
for (n = 0; n < 5; ++n) {
newrow = document.createElement("tr");
newrow.innerHTML = "<td>new row #" + n + "</td>";
refElement.parentNode.insertBefore(newrow, refElement.nextSibling);
}
}
<table>
<tbody>
<tr id="id1">
<td>refElement</td>
</tr>
<tr>
<td>original next sibling</td>
</tr>
</tbody>
</table>
Note the order in which those appeared. If you want them in 0 - 4 order instead:
var id = 1;
var refElement = document.getElementById('id' + id);
var n, newrow;
if (refElement) {
for (n = 0; n < 5; ++n) {
newrow = document.createElement("tr");
newrow.innerHTML = "<td>new row #" + n + "</td>";
refElement.parentNode.insertBefore(newrow, refElement.nextSibling);
refElement = refElement.nextSibling; // *** This is the change
}
}
<table>
<tbody>
<tr id="id1">
<td>refElement</td>
</tr>
<tr>
<td>original next sibling</td>
</tr>
</tbody>
</table>
function addRowAfter(rowId){
var refElement = document.getElementById('id'+id);
var newrow= document.createElement('tr');
refElement.parentNode.insertBefore(newrow, refElement.nextSibling );
return newRow;
}
Check this.
html
<table id="myList">
<thead>
<tr>
<td>Product ID</td>
<td>Product Name</td>
<td>Quantity</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
Javascript
var tableRef = document.getElementById("myList").getElementsByTagName("tbody")[0];
var newRow = tableRef.insertRow(tableRef.rows.length);
var newCell = newRow.insertCell(0);
var otherCell = newRow.insertCell(2);
var check;
var myText = result.text;
var myTextTwo = myText.replace(/['"]+/g, '');
alert(myTextTwo);
for (var i = 0; i < tableRef.rows.length; i++) {
if (myTextTwo != tableRef.rows[i].cells[0].innerHTML) {
check = true
}
else if (myTextTwo == tableRef.rows[i].cells[0].innerHTML) {
tableRef.rows[i].cells[2].innerHTML += 1;
check = false;
break;
}
}
if (check) {
var newText = document.createTextNode(myTextTwo);
var otherText = document.createTextNode("1");
newCell.appendChild(newText);
otherCell.appendChild(otherText);
}
else {
alert("You have scanned this item before.");
}
What I have done is scanning a QR that contains a Product ID(e.g. "123") and insert the Product ID into the column called "Product ID", which I am able to do it.
However, what I am trying to do now is to, if the user scan a QR code that contains the same Product ID(e.g. "123"), my code will be able to detect the duplicate and add onto the quantity.
So what I planned to do is to loop through "Product ID" column and check if there's any duplicate. If there isn't any duplicates, the Quantity for the Product ID would be 1.
Product ID | Product Name | Quantity
123 | Hello | 1
Otherwise, duplicate exist, Quantity would be 2.
Product ID | Product Name | Quantity
123 | Hello | 2
Do you mean something like this?
var tableRef = document.getElementById("myList").getElementsByTagName("tbody")[0];
// save the row number of the existing product
var found = false;
var myText = result.text;
var myTextTwo = myText.replace(/['"]+/g, '');
// search the table for the existing product
for (var i = 0; i < tableRef.rows.length && !found; ++i) {
// if you found it then
if (tableRef.rows[i].cells[0].innerHTML == myTextTwo) {
// update the value
tableRef.rows[i].cells[2].innerHTML += 1;
// and say we found it
found = true;
}
}
// at this point, if we didn't find anything then add a new row
if (!found) {
var newRow = tableRef.insertRow(tableRef.rows.length);
newRow.insertCell(0).innerText = "...";
newRow.insertCell(0).innerText = "...";
newRow.insertCell(0).innerText = 1;
}
<table id="myList">
<thead>
<tr>
<td>Product ID</td>
<td>Product Name</td>
<td>Quantity</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
I think this might help:
The function duplicateCell returns the number of the row that contains a duplicate on the given column or null if no duplicate found.
The function addOne requires a product ID to check for duplicates, a product Name (if no duplicates are found, the product will need a name, otherwise it is ignored) and it also needs to know what table to work on.
AddOne looks for duplicates using the above function and adds one to the quantity.
I started with 1 car 1 bike and 1 skateboard, added 2 bike (id2) and added 2 rockets (id 4).
var duplicateCell = function(tableId, columnNumber, stringToSearch) {
var myTbody = document.getElementById(tableId).getElementsByTagName("tbody")[0];
var tableRows = myTbody.rows.length;
for (var i = 0; i < tableRows; i++) {
if (myTbody.rows[i].cells[columnNumber].innerHTML.trim() == stringToSearch)
return i
}
return null;
}
function addOne(productId, productName, tableId) {
var myTbody = document.getElementById(tableId).getElementsByTagName("tbody")[0];
var rowIndex = duplicateCell(tableId, 0, productId)
if (rowIndex != null) {
myTbody.rows[rowIndex].cells[2].innerHTML = +myTbody.rows[rowIndex].cells[2].innerHTML.trim() + 1;
} else {
var tableRows = myTbody.rows.length;
var row = document.getElementById(tableId).insertRow();
c1 = row.insertCell(0);
c2 = row.insertCell(1);
c3 = row.insertCell(2);
c1.innerHTML = productId;
c2.innerHTML = productName;
c3.innerHTML = 1;
}
}
addOne("4", "Rocket", "myList")
addOne("4", "Rocket", "myList")
addOne("2", " ", "myList")
<table id="myList">
<thead>
<tr>
<td>Product ID</td>
<td>Product Name</td>
<td>Quantity</td>
</tr>
</thead>
<tbody>
<tr>
<td>1 </td>
<td>Car </td>
<td>1 </td>
</tr>
<tr>
<td>2 </td>
<td>Bike </td>
<td>1 </td>
</tr>
<tr>
<td>3 </td>
<td>Skateboard </td>
<td>1 </td>
</tr>
</tbody>
</table>
I have a table. I'd like to compare participants. If participant have several result points in the table, the script has to return sum of all participant's results. And so on for every participant.
The table is generated from database (".$row["pnt"]."".$row["station"]."".$row["res"]."):
Participant Station Points
aa Some1 1
dd Some1 2
aa sm2 3
dd sm2 4
bb sm3 5
ee sm3 6
For example I've to recieve such a new table:
aa - 4,
dd - 6,
bb - 5,
ee - 6
I've tried to do so:
$(document).ready(function () {
$("body").click(function () {
var rows = $("tbody tr");
var jo = [];
for (var i = 0; i < rows.length; i++) {
for (var j = 1; j <= rows.length; j++) {
var pnt1 = $(rows[i]).find(".pnt").html();
var stations1 = $(rows[i]).find(".station").html();
var pntR1 = $(rows[i]).find(".res").html();
if (pnt1 == $(rows[j]).find(".pnt").html()) {
pntR1 = parseInt(pntR1);
pntR2 = parseInt($(rows[j]).find(".res").html());
jo.push(pnt1, pntR1, pntR2);
break;
}
}
}
console.log(jo);
});
});
But I understood that I'm on a wrong way. Please, help me. I really appreicate if some one could help me on this issue.
Updated after comments:
<table id="pntsRes">
<thead>
<tr>
<th>Участники</th>
<th>Баллы</th>
</tr>
</thead>
<tbody>
<tr><td class="pnt">aa</td><td class="station">AES</td><td class="res">1</td></tr><tr><td class="pnt">dd</td><td class="station">AES</td><td class="res">2</td></tr>
<tr><td class="pnt">aa</td><td class="station">Science</td><td class="res">3</td></tr>
<tr><td class="pnt">dd</td><td class="station">Science</td><td class="res">4</td></tr><tr><td class="pnt">bb</td><td class="station">Аэродром</td><td class="res">5</td></tr>
<tr><td class="pnt">ee</td><td class="station">aeroport</td><td class="res">6</td></tr></tbody>
</table>
First, I would consider breaking your solution into three functions - one to extract the data from the HTML (which is a questionable practice in itself), one to transform the data, and one to output the new table. This way, your code is much more maintainable.
function getData() {
var rows = $("tbody tr");
var data = [];
rows.each(function(idx, row){
var pnt = row.find('.pnt').html(),
station = row.find('.station').html()),
res = parseInt(row.find('.res').html());
data.push(pnt, station, res);
});
}
Then I would consider something like this for the second method
// Pass the output from getData() into processData()
function processData(data){
var groupedKeys = {};
var groupedData = data.map(function(datum){
var name = datum[0];
var value = datum[2];
groupedKeys[name] = (groupedKeys[name] || 0) + (value || 0);
});
var transformedData = [];
Object.keys(groupedKeys).forEach(function(key){
transformedData.push([key, groupedKeys[key]]);
});
return transformedData;
}
The last method of course would need to be implemented by yourself, there's a ton that could be improved here, but it could be a good start.
I used an associative array (which is just an object in JavaScript) shown below:
http://jsfiddle.net/a5k6w300/
Changes I made:
var jo = [];
changed to an object instead of an array
var jo = {};
I also added the if(isNaN(object[key]) inside the inner loop in order to make sure that these didn't show as NaN as I continued adding them together.
$(document).ready(function () {
$("body").click(function () {
var rows = $("tbody tr");
var jo = {};
console.log(rows);
for (var i = 0; i < rows.length; i++) {
for (var j = 1; j <= rows.length; j++) {
var pnt1 = $(rows[i]).find(".pnt").html();
var stations1 = $(rows[i]).find(".station").html();
var pntR1 = $(rows[i]).find(".res").html();
if (pnt1 == $(rows[j]).find(".pnt").html()) {
pntR1 = parseInt(pntR1);
pntR2 = parseInt($(rows[j]).find(".res").html());
if(isNaN(jo[pnt1])){
jo[pnt1] = 0;
}
jo[pnt1] += pntR1;
break;
}
}
}
console.log(jo);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="pntsRes">
<thead>
<tr>
<th>Участники</th>
<th>Баллы</th>
</tr>
</thead>
<tbody>
<tr>
<td class="pnt">aa</td>
<td class="station">AES</td>
<td class="res">1</td>
</tr>
<tr>
<td class="pnt">dd</td>
<td class="station">AES</td>
<td class="res">2</td>
</tr>
<tr>
<td class="pnt">aa</td>
<td class="station">Science</td>
<td class="res">3</td>
</tr>
<tr>
<td class="pnt">dd</td>
<td class="station">Science</td>
<td class="res">4</td>
</tr>
<tr>
<td class="pnt">bb</td>
<td class="station">Аэродром</td>
<td class="res">5</td>
</tr>
<tr>
<td class="pnt">ee</td>
<td class="station">aeroport</td>
<td class="res">6</td>
</tr>
</tbody>
</table>
This refers to my previous question.
How to highlight/color multiple rows on selection?
<table id="toppings" border="1" cellpadding="2">
<tr id="id1">
<td>3</td>
<td>row12</td>
<td>row13</td>
</tr>
<tr id="id2">
<td>12</td>
<td>row22</td>
<td>row23</td>
</tr>
<tr id="id3">
<td>15</td>
<td>row32</td>
<td>row33</td>
</tr>
<tr id="id4">
<td>22</td>
<td>row42</td>
<td>row43</td>
</tr>
<tr id="id5">
<td>23</td>
<td>row52</td>
<td>row53</td>
</tr>
<tr id="id6">
<td>55</td>
<td>row62</td>
<td>row63</td>
</tr>
</table>
Javascript Code:
//Get list of rows in the table
var table = document.getElementById("toppings");
var rows = table.getElementsByTagName("tr");
var selectedRow;
//Row callback; reset the previously selected row and select the new one
function SelectRow(row) {
if (selectedRow !== undefined) {
selectedRow.style.background = "#d8da3d";
}
selectedRow = row;
selectedRow.style.background = "white";
}
//Attach this callback to all rows
for (var i = 0; i < rows.length; i++) {
var idx = i;
rows[idx].addEventListener("click", function(){SelectRow(rows[idx])});
}
But this time I have added an event to table for row selection and trying to get min and max value from selected rows (first column). Like above table, if I select middle four rows, i should get min = 12 and max = 23. How can this be implemented?
You can have two functions. I show the getMinValueExample().
function getMinValueExample(rows){
var minValue = null;
for (var i = 0; i < rows.length; i++){
var firstTd = rows[i].getElementsByTagName('td')[0];
var currentValue = parseInt(firstTd.innerHTML);
if(minValue == null || minValue > currentValue)
minValue = currentValue;
}
return minValue;
}
(not test so can contain some type errors but you should get the idea)
So if you call this after you've declared rows it returns the min value.
And if you call this one you get the max value
function getMaxValueExample(rows){
var maxValue = null;
for (var i = 0; i < rows.length; i++){
var firstTd = rows[i].getElementsByTagName('td')[0];
var currentValue = parseInt(firstTd.innerHTML);
if(maxValue == null || maxValue < currentValue)
maxValue = currentValue;
}
return maxValue;
}