I have an HTML table like this:
SALES RENTS
ROME MILAN ROME MILAN MONEY
The HTML code is the following:
<TR>
<TD CLASS=HD1 COLSPAN=2>SALES</TD>
<TD CLASS=HD1 COLSPAN=2>RENTS</TD>
<TD CLASS=HDCOLSEP> </TD>
</TR>
<TR>
<TD>ROME</TD>
<TD>MILAN</TD>
<TD>ROME</TD>
<TD>MILAN</TD>
<TD>MONEY</TD>
</TR>
What I need, is to create with javascript an array like this:
(ROME-SALES, MILAN-SALES, ROME-RENTS, MILAN-RENTS, MONEY).
I have already created the array that contains the element of the first row.
Below you can find my code as it was in the past (At the beginning I needed just to take the elements of the first TR). Now I need to modify it and to create an array as specified before.
I don't know if it is clear from the table, but the first ROME and MILAN columns are referred to the column SALES, the second ROME and MILAN are referred to the RENTS column, while MONEY doesn't have any dependence.
Do you have any idea to do this?
Thanks in advance.
function getColumnsVal(id) {
var header = $("table#" + id + " thead tr:eq(1)");
var header_fields = $("td", header);
var header_vals = [];
header_fields.each(function(idx, val) {
var $$ = $(val);
header_vals.push($$.text());
});
return header_vals;
}
It is definitely possible to read values from the table cells. I edited the post a bit to illustrate the code.
I presume that HTML structure is rigid and you always have two rows of titles in the thead and somewhat random number of merged cells in the first row.
You’d want to match the number of columns in both rows, i.e. take the colspan number into account when traversing the cells.
Read both rows and generate strings by combining cell values in corresponding columns.
For example:
function readTableRow(row) {
var values = [];
$("td", row).each(function(index, field) {
var span = $(field).attr("colspan");
var val = $(field).text();
if (span && span > 1) {
for (var i = 0; i<span; i++ ) {
values.push(val);
}
} else {
values.push(val);
}
});
return values;
}
function getColumnsVal(id) {
// Read the first row, taking colspans into account
var first_row = $("table#" + id + " thead tr:eq(0)");
var first_row_vals = readTableRow(first_row);
// Read the second row, taking colspans into account
var second_row = $("table#" + id + " thead tr:eq(1)");
var second_row_vals = readTableRow(second_row);
if (first_row_vals.length != second_row_vals.length) {
return null;
}
var results = [];
for (var i = 0; i<first_row_vals.length; i++) {
results.push([first_row_vals[i].trim(), second_row_vals[i].trim()].filter(function (el) {return el}).join("-"));
}
return results;
}
function displayResults(results) {
var result = "RESULT: <br />";
results.forEach(function(r) {
result = result + r + "<br />";
});
$("#result").html(result);
}
displayResults(getColumnsVal("sample"));
JSFiddle:
https://jsfiddle.net/adanchenkov/n61dqfrs/
I've been debugging for some time, trying to get the value of a column in a table. I think once I've done this, it should be easy to pass the value in the next column out of my JS function.
My HTML table is:
<table id="country_LE_table" style = "display:none">
<tr>
<td>Japan</td>
<td>83.7</td>
</tr>
<tr>
<td>Switzerland</td>
<td>83.4</td>
</tr>
</table>
My Javascript is:
<script type="text/javascript">
function getLifeExpectancy() {
var Country = "<?php echo $Country ?>";
document.write(Country); // this gets read OK
var table = document.getElementById("country_LE_table");
var tr = table.getElementsByTagName("tr");
document.write(tr.length); document.write("<br>"); // works as expected
for (var i = 0; i < tr.length; i++) {
document.write(tr[i].innerHTML); document.write("<br>"); // works well up to here
// the following doesn't work
var td = tr[i].getElementsByTagName("td")[0];
if (td = Country) { //need td.fullHTML/value/fullText?
return tr[i].getElementsByTagName("td")[1]; // return the number
}
document.getElementById("demo").innerHTML = getLifeExpectancy();
</script>
If I do document.write(td), I get "[object HTMLTableCellElement]" on my page.
If I do document.write(td.fullHTML) I get "undefined" on my page.
When I explore other methods than td.innerHTML, I get this - it looks like I can't use functions based around text.
Use this instead. you have used assignment operator instead of comparison operator "=="
if (td.innerHTML == Country)
{
}
I'm working on a personal project and I've run into an issue that I haven't been able to solve.
Here is a function that generates new table rows into a table (with id of "tableData") when a button is clicked:
function addNewRow(){
var tableEl = document.getElementById("tableData");
var newLine = '<tr class="newEntry">';
var classArray = ["classA", "classB", "classC", "classD"];
for (var i = 0; i < classArray.length; i++){
newLine += '<td><input class="' + classArray[i] + '"></td>';
}
newLine += '</tr>';
tableEl.insertAdjacentHTML("beforeend", newLine);
}
document.getElementById("addRow").addEventListener("click", addNewRow, false);
//the element with id="addRow" is a button
I've simplified the code for the above function for the sake of readability as it's not the focus of the problem. When the button is clicked, a new row is added successfully.
The problematic part involves another function that takes the sum of the respective classes of each row and displays them in a div.
The goal is to get the sum of the values of all input fields with matching class names. For example, let's say I use the addNewRow function to get six rows. Then I want to have the div showing the sum of the values of all input fields with the class name of "classA"; the number in that div should be the sum of those six values, which gets updated as I type in the values or change the existing values in any of the input fields with class name of "ClassA".
function sumValues(divId, inputClass){
var sumVal = document.getElementsByClassName(inputClass);
var addedUp = 0;
for (var j = 0; j < sumVal.length; j++){
addedUp += Number(sumVal[j].value);
}
document.getElementById(divId).innerHTML = addedUp;
}
Here are a couple (out of several) failed attempts:
document.input.addEventListener("keyup", sumValues("genericDivId", "classA"), false);
document.getElementsByClassName("classA").onkeyup = function(){sumValues("genericDivId", "classA");}
Unfortunately, after scouring the web for a solution and failing to find one, I just added an event listener to a button that, when clicked, would update the div to show the sum of values. Also had to modify the sumValues function to take values from an array rather than accepting arguments.
My question is: How can I modify the code so that the sum value updates as I type in new values or change existing values using pure Javascript (vanilla JS)?
You are very close, document.getElementsByClassName() returns an array of DOM objects, you need to set the onkeyup function for each and every element by looping through that array.
var classA = document.getElementsByClassName('classA'); // this is an array
classA.forEach(function(elem){ // loop through the array
elem.onkeyup = function(){ // elem is a single element
sumValues("genericDivId", "classA");
}
}
Hopefully this fixes your issue
Maybe the example below is not same with your situation, but you'll get the logic, easily. Anyway, do not hesitate to ask for more guide.
document.getElementById("row_adder").addEventListener("click", function() {
var t = document.getElementById("my_table");
var r = t.insertRow(-1); // adds rows to bottom - change it to 0 for top
var c = r.insertCell(0);
c.innerHTML = "<input class='not_important_with_that_way' type='number' value='0' onchange='calculate_sum()'></input>";
});
function calculate_sum() {
var sum = ([].slice.call(document.querySelectorAll("[type=number]"))).map(e=>parseFloat(e.value)).reduce((a, b) => a+b);
document.getElementById("sum").innerHTML = sum;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div>
<p>
<strong>Sum</strong>:<span id="sum">0</span>
</p>
</div>
<button id="row_adder">
Click me
</button>
<table id="my_table">
</table>
</body>
</html>
I have a tablesorter table that is generated dynamically with javascript and ajax. There are inputs for the user to change values, and when they click a button to go to the next or previous record, it needs to save the information in the table to a MySQL table. I have looked at many of the posts on here and tried numerous examples, and I still can't get any of the data in the table to post to my PHP page to save it.
Here is a sample of the first row of my table:
$("#guides").append('<tr><td id="amCodeOld">'+data.amCodeOld+'</td><td><input type="text" class="qty" id="amOldQty"/></td><td>'+data.amOldPrice+
'</td><td>'+data.am+
'</td><td>'+data.amCodeNew+'</td><td><input type="text" class="qty" id="amNewQty"/></td><td>'+data.amNewPrice+
'</td><td><input type="checkbox" id="amS" '+
'"/></td><td><input type="checkbox" id="amR"'+'"/></td><td><input type="checkbox" id="amCall"'+
'"/></td><td><input type="text" id="amComm" value="'+
'"/></td></tr>');
There are more rows like this one, each for a different guide I am using. What I need to find is the value of the inputs with id of amOldQty, amNewQty etc, and the different checkboxes.
I have buttons for Next, Prev, and Print, and when the button is clicked I am trying to call a function called save() that will get the info, and send it via ajax to another PHP page to be saved.
save() looks like this:
function save() {
var amOldQty
$('#guides tr').each(function(){
alert("searching ");
amOldQty= $(this).find('#amOldQty').val();
if (amOldQty=='' || amOldQty== null) {
amOldQty = "Not Showing Number";
}
console.log(amOldQty);
});// END OF EACH FUNCTION
I have tried:
var amOldQty;
$('#guides tbody tr').each(function() {
amOldQty = $(this).find('td:nth-child(1) input').val();
});
console.log(amOldQty);
and the log shows undefined. I have also tried a more direct approach by using
$('#guides tbody tr').each(function() {
amOldQty = $(this).find('#amOldQty').val();
});
and still have nothing. I am getting the alert of Searching, but in the console.log(amQtyOld) all it shows me is "Not Showing a number". I have even tried to have the input populated when the table first is created, and it still does not find that number.
Update if I add td to:
$(guides tr td).each(function(){...
It does not even give me the alert of "Searching".
How do I get all the data out of this table so I can save it? it seems that everything I am trying should be working.
If your users use browsers that support contenteditable elements, then you can check out the contenteditable widget I have available for my fork of tablesorter.
If you don't want to use contenteditable elements, then you can try the following code (copied and modified from the contenteditable demo, but not tested)
var $table = $('table');
$table.children('tbody').on('change', 'input', function(event){
var $input = $(this),
$cell = $input.closest('td'),
newContent = $input.val(),
cellIndex = $cell[0].cellIndex, // there shouldn't be any colspans in the tbody
rowIndex = $this.closest('tr').attr('id'); // unique row id
// update tablesorter
$table.trigger("updateCell", [$cell]);
$.post("mysite.php", {
"row" : rowIndex,
"cell" : cellIndex,
"content" : newContent
});
});
Hopefully, you're also using an input parser for that column.
The final answer seems to be to just hardcode the value of the inputs $("#amOldQty:).val() and pass them as data through ajax to my php file to save the information. Here is the completed code in case anyone has a similar issue:
function save() {
function GuidesSave(){
this.update();
}
GuidesSave.prototype.list = ['am','fol','tich','book','neb','ster','c','byte','ing'];
GuidesSave.prototype.update = function(){
for( var i = 0 ; i < this.list.length ; i++ ){
var guide = this.list[i];
this[ guide + 'S' ] = $("#" + guide+'S' ).is(":checked") ? 1 : 0;
this[ guide + 'R' ] = $("#" + guide+'R' ).is(":checked") ? 1 : 0;
this[ guide + 'Call' ] = $("#" + guide+'Call' ).is(":checked") ? 1 : 0;
}// end of for loop
}
var guides = new GuidesSave();
$.ajax({
type: "POST",
url: "poSave.php",
dataType: "json",
data: ({po: $('#po').val(),isbn:$("#isbn13").val(),amOldQty:$("#amOldQty").val(),amNewQty:$("#amNewQty").val(),amS:guides.amS,amR:guides.amR, amCall:guides.amCall,amComm:$("#amComm").val(),
folOldQty:$("#folOldQty").val(),folNewQty:$("#folNewQty").val(),folS:guides.folS,folR:guides.folR, folCall:guides.folCall,folComm:$("#folComm").val(),
tichOldQty:$("#tichOldQty").val(),tichNewQty:$("#tichNewQty").val(),tichS:guides.tichS,tichR:guides.tichR, tichCall:guides.tichCall,tichComm:$("#tichComm").val(),
bookOldQty:$("#bookOldQty").val(),bookNewQty:$("#bookNewQty").val(),bookS:guides.bookS,bookR:guides.bookR, bookCall:guides.bookCall,bookComm:$("#bookComm").val(),
nebOldQty:$("#nebOldQty").val(),nebNewQty:$("#nebNewQty").val(),nebS:guides.nebS,nebR:guides.nebR, nebCall:guides.nebCall,nebComm:$("#nebComm").val(),
sterOldQty:$("#sterOldQty").val(),sterNewQty:$("#sterNewQty").val(),sterS:guides.sterS,sterR:guides.sterR, sterCall:guides.sterCall,sterComm:$("#sterComm").val(),
cheggOldQty:$("#cOldQty").val(),cheggNewQty:$("#cNewQty").val(),cheggS:guides.cS,cheggR:guides.cR, cheggCall:guides.cCall,cheggComm:$("#cComm").val(),
byteOldQty:$("#byteOldQty").val(),byteNewQty:$("#byteNewQty").val(),byteS:guides.byteS,byteR:guides.byteR, byteCall:guides.byteCall,byteComm:$("#byteComm").val(),
ingOldQty:$("#ingOldQty").val(),ingNewQty:$("#ingNewQty").val(),ingS:guides.ingS,ingR:guides.ingR, ingCall:guides.ingCall,ingComm:$("#ingComm").val(),
qty1: $('#topqty').val(),price1: $('#topVal').html(),comp1:$('#topCo').html(),
qty2: $('#secqty').val(),price2: $('#secVal').html(),comp2: $('#secCo').html(),
qty3: $('#thrqty').val(),price3: $('#thrVal').html(),comp3: $('#thrCo').html()}),
success: function(data){
}
});
}// END OF SAVE FUNCTION
The GuideSave function loops through all the check boxes (27 different ones) to see whichones are checked so I can save them as either a 1 or 0 and then have them checked or not when the record is recalled.
It is not really clear what or how you are wanting to present the data. However, here is a JSFiddle that does what it appears you desire.
The function createRowsForTesting() just creates the rows in the table and populates the <input> fields to make testing easier.
The function getDataFromTable() walks the rows of a <table>, or all <tables>, sending the data you said you wanted to the console. From your own answer to this question it became clear that you really wanted to access many more <input> elements than was mentioned in your question. Thus, this this function now builds an array of Objects which contain key:value pairs for the data in <input> fields. There is one Object in the array for each row. The array is returned to the calling function.
The function wrapGetDataFromTable() wraps getDataFromTable() providing the table ID, that we are only looking in rows within the <tbody> tag and that we want output to the console. The array returned by getDataFromTable() is output so we can see the data structure. The function is set up to run each time you press the [print to console] button.
For a table that looks like (with header row):
The output is:
amOldQty=amOldQty0_text amNewQty=amNewQty0_text amS=on amR=off amCall=off amComm=amComm0_text
amOldQty=amOldQty1_text amNewQty=amNewQty1_text amS=off amR=on amCall=off amComm=amComm1_text
amOldQty=amOldQty2_text amNewQty=amNewQty2_text amS=off amR=off amCall=on amComm=amComm2_text
The array of row input data objects is:
[0] Object { amOldQty="amOldQty0_text", amNewQty="amNewQty0_text", amS="on", more...}
amOldQty: "amOldQty0_text"
amNewQty: "amNewQty0_text"
amS: "on"
amR: "off"
amCall: "off"
amComm: "amComm0_text"
[1] Object { amOldQty="amOldQty1_text", amNewQty="amNewQty1_text", amS="off", more...}
amOldQty: "amOldQty1_text"
amNewQty: "amNewQty1_text"
amS: "off"
amR: "on"
amCall: "off"
amComm: "amComm1_text"
[2] Object { amOldQty="amOldQty2_text", amNewQty="amNewQty2_text", amS="off", more...}
amOldQty: "amOldQty2_text"
amNewQty: "amNewQty2_text"
amS: "off"
amR: "off"
amCall: "on"
amComm: "amComm2_text"
JavaScript:
/**
* Runs through a table getting the values from <input> fields.
* It only looks in the <tbody>, not the <thead>
* #param tableId
* The DOM ID of the table from which we desire to obtain the
* input values.
* If tableId is not a string, then look in all table rows in all tables.
* #param keyAttr
* The attribute of the <input> which contains the value which will
* be used as the key for the key:value pair within the Object returned.
* This needs to be a value which is unique, wihin the table row.
* A normal use would be "id".
* If a value is duplicated a message is sent to the console and only
* the last value is kept.
* The default is "id".
* #param justBody
* If true, look only within the <tbody> tag, not any other part of
* the table (e.g. <thead>).
* The default is true.
* #param includeBlank
* Boolean indicating if the returned array should contain an entry for
* rows which are found to be blank.
* The default is true.
* #param consoleOutput
* Send a line to the console with the key:value pairs separated by
* tabs for each row.
* The default is false.
* #return Object
* Returns an Array of Objects with key:value pairs for the rows.
* If there were no <input>
* Copyright 2014 by Makyen.
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/.
*/
function getDataFromTable(tableId, keyAttr, justBody, includeBlank, consoleOutput) {
//This assumes that within the row each input has a unique attribute keyAttr.
//Set defaults:
var tableSelector = (typeof tableId === "string") ? "#" + tableId : "table";
keyAttr = (typeof keyAttr === "string") ? keyAttr : "id";
includeBlank = (typeof includeBlank === "boolean") ? includeBlank : true;
justBody = (typeof justBody === "boolean") ? justBody : true;
consoleOutput = (typeof consoleOutput === "boolean") ? consoleOutput : false;
var bodySelector = (justBody) ? " tbody" : "";
var toReturn = [];
var selector = tableSelector + bodySelector + ' tr';
$(selector).each(function () {
var inputs = {};
$(this).find('input').each(function () {
//Get the value for all inputs on this line.
var attrValue = $(this).attr(keyAttr);
if (typeof inputs[attrValue] !== "undefined") {
console.log("Warning: When attempting to get data from the table id=" //
+ tableId + " the value of the key attribute, " + keyAttr //
+ ", was not unique for value=" + attrValue);
}
//Get the value of the <input>.
if ($(this).is(':checkbox')) {
//Special case the checkboxes because .val() does not return
//the correct informaiton for them.
//First indicate that all checkboxes are off.
inputs[attrValue] = "off";
//Specifically determine if the current one is checked.
if ($(this).is(':checked')) {
inputs[attrValue] = "on";
}
} else {
//Add this input to the object
inputs[attrValue] = $(this).val();
}
});
var inputKeys = Object.keys(inputs);
if (inputKeys.length > 0) {
//There were <input> tags on this row.
var outputText = "";
if (consoleOutput) {
inputKeys.forEach(function (value) {
outputText += value + "=" + inputs[value] + "\t";
});
console.log(outputText);
}
toReturn.push(inputs);
} else {
//No <input> tags on this row
if (includeBlank) {
if (consoleOutput) {
console.log("A row without <input> tags was found.");
}
toReturn.push(inputs);
}
}
});
return toReturn;
}
function wrapGetDataFromTable() {
//This wraper is so the getDataFromTable() function remains
// generic. The wrapper merely defines which table from which to
// get the data,
// the attribute to use for unique keys = "id"
// to look only in the <tbody>
// to not include an object for the lines which are blank
// and output the row data to the console.
var toReturn = getDataFromTable("guides", "id", true, false, true);
if (typeof console.dir === "function") {
//Make sure console.dir() exists prior to using it.
console.log("The array of row input data objects is:");
console.dir(toReturn); //Let us see the Object in the console for checking.
}
return toReturn;
}
$('#to-console-button').click(wrapGetDataFromTable);
//The rest is setup for creating the table header and rows.
//It is only for testing.
function createRowsForTesting() {
const numRowsToCreate = 3;
var i;
var data = {
amCodeOld: "amCodeOld",
amOldPrice: "amOldPrice",
am: "am",
amCodeNew: "amCodeNew",
amNewPrice: "amNewPrice"
};
//Create the table
//First add a header.
$("#guides thead").append('<tr><th>amCodeOld_H</th>' //
+ '<th>amOldQty_H</th>' //
+ '<th>amOldPrice_H</th>' //
+ '<th>am_H</th>' //
+ '<th>amCodeNew_H</th>' //
+ '<th>amNewQty_H</th>' //
+ '<th>amNewPrice_H</th>' //
+ '<th>amS_H</th>' //
+ '<th>amR_H</th>' //
+ '<th>amCall_H</th>' //
+ '<th>amComm_H</th></tr>');
//Now the body rows.
for (i = 0; i < numRowsToCreate; i++) {
//From stackoverflow question: http://stackoverflow.com/questions/25998929/extract-data-from-a-tablesorter-table-with-javascript
$("#guides tbody").append('<tr><td id="amCodeOld">'+data.amCodeOld+'</td><td><input type="text" class="qty" id="amOldQty"/></td><td>'+data.amOldPrice+ //
'</td><td>'+data.am+ //
'</td><td>'+data.amCodeNew+'</td><td><input type="text" class="qty" id="amNewQty"/></td><td>'+data.amNewPrice+ //
'</td><td><input type="checkbox" id="amS" '+ //
'"/></td><td><input type="checkbox" id="amR"'+'"/></td><td><input type="checkbox" id="amCall"'+ //
'"/></td><td><input type="text" id="amComm" value="'+ //
'"/></td></tr>');
}
//*
//Fake having the table filled in, as I am tired of entering the input
//You have to try it without this, but with manual input in order to truly verify
var row = 0;
$('#guides tbody tr').each(function () {
$(this).find('#amOldQty').val("amOldQty" + row + "_text");
$(this).find('#amNewQty').val("amNewQty" + row + "_text");
$(this).find('#amComm').val("amComm" + row + "_text");
row++;
});
//*/
}
createRowsForTesting();
HTML:
<table class="tablesorter" id="guides">
<thead></thead>
<tbody></tbody>
</table>
<button type="button" id="to-console-button">print to console</button>
<!-- values to use to fill input:
amOldQty0_text
amOldQty1_text
amNewQty0_text
amNewQty1_text
amComm0_text
amComm1_text -->
Note: The selector ($("#guides").append('<tr>...) used in your line to add the table row to the table might be your problem. It currently adds your rows as the last elements in the <table>, not the last in the <tbody>. While the browser should compensate for this, it is possible that it is not doing so in your environment. Try $("#guides tbody").append('<tr>...
However, it appears more likely that the issue is a header row (or a row without input cells) in the <tbody>. The code now accounts for this possibility.
I have a report populated as a table with a stringbuilder from the codebehind. The first TD of every row is a checkbox, the id of each checkbox is assigned dynamically:
sb.Append("<td><input type='checkbox' id='chkSelectAll_" + i + "' name='chk_" + i + "' onclick='JavaScript: chkAll_click(this);' /> </td>"
The aspx page uses a master page and
<asp:Content><div id='divMain'></div></asp:Content>
format other than a form to populate. The problem I am running in to is that I am having trouble finding all the elements (or any actually) of the div to work with. Here is the javascript I have been given. (Team project at work, I was just assigned 1 task on the project so changing anything is not an option.)
function divBatchBuild_click() {
debugger
var form = document.forms[0];
var visitList = '';
for (i = 0; i < form.elements.length; i++) {
if (form.elements[i].type == 'checkbox') {
//alert(form.elements[i].id.toString());
if (form.elements[i].checked == true &&
form.elements[i].id != 'chkSelectAll') {
var y = form.elements[i].id;
//alert('id=' + y[1].toString());
visitList = visitList + y[i].toString() + '|';
}
}
}
}
Apparently this worked on a previous project, but when used with this report the process never goes inside the if statement. Any help on what is going wrong is appreciated.
I think you want to first get the div, then get the elements in the div with the checkbox tagname. Something like:
var div = document.getElementById('divMain');
var elements = div.getElementsByTagName('checkbox');
for (i = 0; i < elements.length; i++) {