Can't seem to use element IDs generated dynamically - javascript

I want to create a tiered pay calculator utility.
In my Javascript file I dynamically add rows and fields to a table I created with the HTML from a list of names I have in a text input field. The rows consist of a name label column, a text input column to put they're gross earnings in and a calculated pay output column.
I would like for it to update the calculated pay column automatically when I make a change in the text input in the gross earnings column. Although I can trace the element ids through to the calculatePay function I can't seem to use or set their properties. I get the feeling they are not unique as well. Any ideas?
BTW you have to click the update button right now to run the Javascript.
Edit - made some changes per suggestion. Still can't seem to take variables passed to the calculatePay function and simply turn them around and spit them back out into the Pay column of my table.
Edit - SOLVED. The issue was closure, which I didn't understand at first but here's the skinny. To isolate the scope of the variables so they don't get re-wrote every time the loop comes around they need to be declared inside a function that gets recreated with every loop iteration (because Javascript scopes to the function instead of to the code block). Things to note - this does not work if you just declare a nameless function in the middle of your loop. You must return it to a variable(i.e. var buildElement = function(){Yada Yada}();). Also, after the function add () to execute it.
Javascript
function buildTable(){
var artists = document.getElementById("artlist").value;
var names = artists.split(",");
var len = names.length;
var ptable = document.getElementById("payTable");
var rowLength = ptable.rows.length;
for (i=0 ; i < len; i++){
var buildElement = function(){
var row = ptable.insertRow(rowLength);
var nameCell = row.insertCell(0);
var grossCell = row.insertCell(1);
var payCell = row.insertCell(2);
var grossText = document.createElement("input");
grossText.type = "type";
grossText.name = "gtext[]";
grossText.id = "gross" + names[i];
payCell.id = "pay" + names[i];
grossText.onchange = function(){calculatePay(payCell.id, grossText.id);};
grossCell.appendChild(grossText);
nameCell.innerHTML = names[i];
}();
}
}
function resetTable(){
var ptable = document.getElementById("payTable");
var rowLength = ptable.rows.length;
if (rowLength>2){
for (p=rowLength; p>2; p--){
ptable.deleteRow(2);
}
}
buildTable();
}
function calculatePay(target, gross) {
document.getElementById(target).innerHTML = document.getElementById(gross).value;
}
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="./js/script.js"></script>
<link rel="stylesheet" href="./css/style.css" />
</head>
<body>
<div>
<table id="payTable">
<tr>
<th colspan=3 class="hdr">
Calculator
</th>
</tr>
<tr>
<th class="hdr">
Name
</th>
<th class="hdr">
Gross
</th>
<th class="hdr">
Pay
</th>
</tr>
</table>
</div>
<div>
<table>
<tr>
<th colspan=3 class="hdr">
SETTINGS
</th>
</tr>
<tr>
<th colspan=3 class="hdr">
Breakpoints
</th>
</tr>
<tr>
<td>Break 1 at $
<input type="text" name="break1" id="break1" value="300"/>
</td>
<td>
</td>
<td>Break 2 at $
<input type="text" name="break2" id="break2" value="900"/>
</td>
</tr>
<tr>
<th colspan=3 class="hdr">
Percentage levels
</th>
</tr>
<tr>
<td>
Below break 1:
<input type="text" size="4" name="per1" id="per1" value="50"/>%
</td>
<td>
Between breaks 1 and 2:
<input type="text" size="4" name="per2" id="per2" value="60"/>%
</td>
<td>
Over break 2:
<input type="text" size="4" name="per3" id="per3" value="70"/>%
</td>
</tr>
<tr>
<td>
Artists:
<input type="text" name="artlist" id="artlist" value="Brian,Eric,Christie,Cynthia,Shawn"/>
</td>
<td>
</td>
<td class="hdr">
<button onclick="resetTable()">Update</button>
</td>
</tr>
</div>
</body>
</html>

The issue was closure, which I didn't understand at first but here's the skinny. To isolate the scope of the variables so they don't get re-wrote every time the loop comes around they need to be declared inside a function that gets recreated with every loop iteration (because javascript scopes to the function instead of to the code block). Things to note - this does not work if you just declare a nameless function in the middle of your loop. You must return it to a variable(i.e. "var buildElement = function(){Yada Yada}();"). Also, after the function add () to execute it.
Javascript
function buildTable(){
var artists = document.getElementById("artlist").value;
var names = artists.split(",");
var len = names.length;
var ptable = document.getElementById("payTable");
var rowLength = ptable.rows.length;
for (i=0 ; i < len; i++){
var buildElement = function(){
var row = ptable.insertRow(rowLength);
var nameCell = row.insertCell(0);
var grossCell = row.insertCell(1);
var payCell = row.insertCell(2);
var grossText = document.createElement("input");
grossText.type = "type";
grossText.name = "gtext[]";
grossText.id = "gross" + names[i];
payCell.id = "pay" + names[i];
grossText.onchange = function(){calculatePay(payCell.id, grossText.id);};
grossCell.appendChild(grossText);
nameCell.innerHTML = names[i];
}();
}
}
function resetTable(){
var ptable = document.getElementById("payTable");
var rowLength = ptable.rows.length;
if (rowLength>2){
for (p=rowLength; p>2; p--){
ptable.deleteRow(2);
}
}
buildTable();
}
function calculatePay(target, gross) {
document.getElementById(target).innerHTML = document.getElementById(gross).value;
}
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="./js/script.js"></script>
<link rel="stylesheet" href="./css/style.css" />
</head>
<body>
<div>
<table id="payTable">
<tr>
<th colspan=3 class="hdr">
Calculator
</th>
</tr>
<tr>
<th class="hdr">
Name
</th>
<th class="hdr">
Gross
</th>
<th class="hdr">
Pay
</th>
</tr>
</table>
</div>
<div>
<table>
<tr>
<th colspan=3 class="hdr">
SETTINGS
</th>
</tr>
<tr>
<th colspan=3 class="hdr">
Breakpoints
</th>
</tr>
<tr>
<td>Break 1 at $
<input type="text" name="break1" id="break1" value="300"/>
</td>
<td>
</td>
<td>Break 2 at $
<input type="text" name="break2" id="break2" value="900"/>
</td>
</tr>
<tr>
<th colspan=3 class="hdr">
Percentage levels
</th>
</tr>
<tr>
<td>
Below break 1:
<input type="text" size="4" name="per1" id="per1" value="50"/>%
</td>
<td>
Between breaks 1 and 2:
<input type="text" size="4" name="per2" id="per2" value="60"/>%
</td>
<td>
Over break 2:
<input type="text" size="4" name="per3" id="per3" value="70"/>%
</td>
</tr>
<tr>
<td>
Artists:
<input type="text" name="artlist" id="artlist" value="Brian,Eric,Christie,Cynthia,Shawn"/>
</td>
<td>
</td>
<td class="hdr">
<button onclick="resetTable()">Update</button>
</td>
</tr>
</div>
</body>
</html>

Related

Clone table and display after button

Great day Community,i'm facing clone whole table problem, if it have solution of clone several row it will be helping a lots.
If using document.getElementsByTagName("table")[2]; it can clone the table and put it in body because i'm using document.body.appendChild(myClone) to do it.
Here is some code:
Solution 1:
function myFunction() {
myTable = document.getElementsByTagName("table")[2]; // doesn't use any table id
myClone = myTable.cloneNode(true);
var y = document.body.appendChild(myClone);
}
Solution 2:
function myFunction() {
var x = document.getElementById("0"); // using this to find auto genereate id for table
test = x.cloneNode(true);
}
Html Display:
<table>
<tr>
<td>
<table id="0">
<tr>
<td><span></span>Name:<input type="text" value="Tom"/> </td>
<td><span> </span>Age:<input type="text" value="25"/> </td>
<td><span> </span>Email:<input type="text" value="tom#gmail.com"/> </td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table id="1">
<tr>
<td><span></span>Name:<input type="text" value="Alice"/> </td>
<td><span> </span>Age:<input type="text" value="22"/> </td>
<td><span> </span>Email:<input type="text" value="alice#gmail.com"/> </td>
</tr>
</table>
<input type="button" onclick="myFunction()"/>
</td>
</tr>
</table>
Expected result clone the table after the button, the table inside will not have more than 5.
Please help thank you.
Although Dominic Amal Joe F's answer was on the right track, it had some flaws, as well as the structure of the OP table. I think this code would work properly:
function myFunction(){
// get main table body
var tableBody = document.getElementById('mytable').children[0];
// get existing rows
var rows = tableBody.children.length;
// clone the last row (which contains the last table)
var newRow = tableBody.children[rows-1].cloneNode(true);
// get the new row table
var newTable = newRow.children[0].children[0]
// change the table id
newTable.setAttribute('id', rows);
// reset the inputs values
var cells = newTable.children[0].children[0].children;
for (var i=0; i<cells.length; i++) {
cells[i].children[1].value = "";
}
// append the new row to the main table body
tableBody.appendChild(newRow);
}
<table id="mytable">
<tr>
<td>
<table id="0">
<tr>
<td><span>Name:</span><input type="text" value="Tom"/></td>
<td><span>Age:</span><input type="number" value="25"/></td>
<td><span>Email:</span><input type="email" value="tom#gmail.com"/></td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table id="1">
<tr>
<td><span>Name:</span><input type="text" value="Alice"/></td>
<td><span>Age:</span><input type="number" value="22"/></td>
<td><span>Email:</span><input type="email" value="alice#gmail.com"/></td>
</tr>
</table>
</td>
</tr>
</table>
<button onclick="myFunction()">Clone</button>
I feel the following code will help you.
HTML
<table>
<button onclick="myFunction()">clone</button>
<tr>
<table id="parent-table">
<tr id="parent-row">
<td><span></span>Name:<input type="text" value="Tom"/> </td>
<td><span> </span>Age:<input type="text" value="25"/> </td>
<td><span> </span>Email:<input type="text" value="tom#gmail.com"/></td>
</tr>
</table>
</tr>
</table>
JavaScript
function myFunction(){
let parentTable = document.getElementById("parent-table");
let parentRow = document.getElementsByTagName('tr')
let clone = parentRow[1].cloneNode(true);
parentTable.appendChild(clone)
}

Getting next element in a table javascript

https://jsfiddle.net/en6jh7pa/1/
I am having issues grabbing the next element, it is returning null for the next element.
I am passing "this? as onclick and I assumed that you could use this to grab the next element but it seems that it instead returns null
Thanks for your help
function assignnames(checkboxelement){
checkboxelement.setAttribute("name", "checkbox");
var value1box = checkboxelement.nextSibling;
value1box.setAttribute("name", "notnull");
var value2box = checkboxelement.nextElementSibling;
value2box.setAttribute("name", "notnull");
alert("done");
}
<table border="1">
<tr>
<th>
Checkbox
</th>
<th>
value1
</th>
<th>
value2
</th>
</tr>
<tr>
<td>
<input type="checkbox" onclick="assignnames(this)" id="checkbox1"/>
</td>
<td>
<input type="text" name="" id="fname1">
</td>
<td>
<input type="text" name="" id="lname1">
</td>
</tr>
</table>
If you want to get the text inputs in the same row, you can go up to the row, then use a selector to get the inputs, e.g.
function getParent(node, tag) {
var tag = tag.toLowerCase();
do {
if (node.tagName.toLowerCase() == tag) {
return node;
}
node = node.parentNode;
} while (node && node.tagName && node.parentNode)
return null;
}
function getInputs(evt) {
var row = getParent(this, 'tr');
var inputs;
if (row) {
inputs = row.querySelectorAll('input[type="text"]');
}
console.log(`Found ${inputs.length} text inputs, node is ${this.checked? '':'not '}checked.`);
}
window.onload = function(){
document.getElementById('checkbox1').addEventListener('click', getInputs, false);
};
<table border="1">
<tr><th>Checkbox
<th>value1
<th>value2
<tr><td><input type="checkbox" id="checkbox1">
<td><input type="text" name="" id="fname1">
<td><input type="text" name="" id="lname1">
</table>
For the inputs to be siblings, they would all have to be within the same <td>, sharing a singular parent. With them spread out across multiple table cells, they would be considered cousins instead (keeping with the family tree metaphor), which doesn't have a similar shortcut property.
You can still use nextElementSibling along the way between inputs, but you'll also have to move up and back down between generations.
function assignnames(checkboxelement){
checkboxelement.setAttribute("name", "checkbox");
var value1box = checkboxelement
.parentElement // up a generation the checkbox' parent <td>
.nextElementSibling // then to the next <td> in the row
.firstElementChild; // and back down a generation to the next input
// the last step could also be: .querySelector('input')
value1box.setAttribute("name", "notnull");
var value2box = value1box
.parentElement
.nextElementSibling
.firstElementChild;
value2box.setAttribute("name", "notnull");
alert("done");
}
<table border="1">
<tr>
<th>
Checkbox
</th>
<th>
value1
</th>
<th>
value2
</th>
</tr>
<tr>
<td>
<input type="checkbox" onclick="assignnames(this)" id="checkbox1"/>
</td>
<td>
<input type="text" name="" id="fname1">
</td>
<td>
<input type="text" name="" id="lname1">
</td>
</tr>
</table>

Changing value of a cell with JavaScript

I am having a problem changing the value of a cell in a HTML table. I am just messing around with JavaScript because I have never used it. Here is my code:
<!DOCTYPE html>
<html>
<head>
<script>
var name = "Requiem";
var health = 100;
var strength = 1;
var agility = 1;
var intelligence = 1;
var gold = 50;
var Class = "Warrior";
document.getElementsByName('Name').innerHTML = name;
</script>
</head>
<body>
<table id="myTable" border="1">
<tr>
<td>Name</td>
<td>Health</td>
<td>Strength</td>
<td>Agility</td>
<td>Intelligence</td>
<td>Gold</td>
<td>Class</td>
</tr>
<tr>
<td name="Name"></td>
<td name="Health"></td>
<td name="Strength"></td>
<td name="Agility"></td>
<td name="Intelligence"></td>
<td name="Gold"></td>
<td name="Class"></td>
</tr>
</table>
</body>
</html>
Your problem is two fold.
Your script tag is in the head and runs immediately. Only tags that have been processed before the script will be available to manipulate. You can fix this by moving your script tag below the <td name="Name"></td> tag or delaying the code with something like jQuery's document ready (requires jQuery).
document.getElementsByName returns a NodeList containing all the elements with the specified name. To manipulate the first element with this name, you can use document.getElementsByName("Name")[0].
Example:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<table id="myTable" border="1">
<tr>
<td>Name</td>
<td>Health</td>
<td>Strength</td>
<td>Agility</td>
<td>Intelligence</td>
<td>Gold</td>
<td>Class</td>
</tr>
<tr>
<td name="Name"></td>
<td name="Health"></td>
<td name="Strength"></td>
<td name="Agility"></td>
<td name="Intelligence"></td>
<td name="Gold"></td>
<td name="Class"></td>
</tr>
</table>
<script>
var name = "Requiem";
var health = 100;
var strength = 1;
var agility = 1;
var intelligence = 1;
var gold = 50;
var Class = "Warrior";
document.getElementsByName('Name')[0].innerHTML = name;
</script>
</body>
</html>
document.getElementsByName() returns a NodeList (notice that it's Elements rather than Element, so you have to specify which element you'd like to modify.
In this case, there's only one element, so you only need to access the first in the list:
document.getElementsByName("Name")[0].innerHTML = name;
var name = "Requiem";
var health = 100;
var strength = 1;
var agility = 1;
var intelligence = 1;
var gold = 50;
var Class = "Warrior";
document.getElementsByName('Name')[0].innerHTML = name;
<!DOCTYPE html>
<html>
<body>
<table id="myTable" border="1">
<tr>
<td>Name</td>
<td>Health</td>
<td>Strength</td>
<td>Agility</td>
<td>Intelligence</td>
<td>Gold</td>
<td>Class</td>
</tr>
<tr>
<td name="Name"></td>
<td name="Health"></td>
<td name="Strength"></td>
<td name="Agility"></td>
<td name="Intelligence"></td>
<td name="Gold"></td>
<td name="Class"></td>
</tr>
</table>
</body>
</html>

incrementing the value of a input field in the cloned row

1)here i'm doing clone of a row...but this code is working only in eclipse [ ie ,cloning is working ] and it is also not working in any browsers.
2)What is the solution to get the values of text boxes in the cloned rows having same name, and insert into the database using jsp and servlet?
how can i get those values with same name
3)i have servlet code to get only single value from jsp
String address_seq_num =request.getParameter("address_seq_num");
how can i get the value of address seq number in the cloned row fromjsp to servlet to insert into the next row of a table in the database.
4)if i mention "DOCUMENT TYPE" to this code ,it will not work in eclipse also.....
please guide me...
JavaScript
function clonetable() {
var x=document.getElementById("main_table"); //get the table
var rowCount = x.rows.length;
var row = document.getElementById("table_row_clone"); // find row to copy
var table = document.getElementById("table_body"); // find table to append to
var clone = row.cloneNode(true); // copy children too
var tb1 = clone.document.getElementById("asn");//here i'm incrementing the value
tb1.value=rowCount+1;//of "address seq num " in the cloned row
clone.id = "abc"; // change id or other attributes/contents
table.appendChild(clone); // add new row to end of table
}
function deltable() {
var x = document.getElementById("main_table"); //get the table
var rowCount = x.rows.length;
if (rowCount > 1) {
x.deleteRow(rowCount - 1);
} //delete the last row
}
HTML
<table id="main_table" align="center" style="width:75%">
<tbody id="table_body">
<tr id="table_row_clone">
<td>
<table align="center" style="width:100%">
<tr>
<td align="center">
<div style="border:3px solid silver;border-radius:5px;background-color:grey">
<table width="100%">
<tr>
<th align="center">Address Details</th>
</tr>
</table>
</div>
</td>
</tr>
<tr>
<td>
<div style="border:3px solid silver;border-radius:5px;background-color:#1E90FF">
<table align="center" style="width:99%">
<tr style="background-color:#1E90FF">
<td style="width:35%">
<table width="100%">
<tr id="slrow">
<td style="width:43%">Address Seq Num</td>
<td>
<input id="asn" style="width:60px" name="address_seq_num" type="text" value="1" readonly>
</td>
</tr>
</table>
</td>
<td width="49%" align="right">
<input style="width:80%" type="text" value="Reg.office/Primary Office">
</td>
<td align="right">
<input style="width:30px" type="button" value="+" onclick="clonetable()">
<input style="width:30px" type="button" value="-" onclick="deltable()">
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
Per your HTML (which is messy, by the way) you can increment that textbox's value with something like:
var tb = document.getElementById("asn");
tb.value = parseInt(tb.value, 10) + 1;
The trick is you have to cast the textbox's value into a number, which is what parseInt is doing in that example.
Note that the above snippet will give you "NaN" in the textbox if the value is not a valid number - it's not doing any data validation at all.

How can I populate a table's cell through JavaScript code?

I have a table as below. I have to populate the "Amount" field using the "Buy Quantity" and "Market Price" field. Amount = Buy Quantity*Market Price. I am doing something as -
<script>
function populate() {
var rows = document.getElementById("mytable").getElementsByTagName("tr");
for ( var i = 1; i <= rows.length; i++) {
cells = rows[i].getElementsByTagName("td");
for ( var j = 0; j <= cells.length; j++) {
if (j == 1) {
var num1 =parseFloat(cells[1].childNodes[0].value);
var num2 =parseFloat(cells[2].childNodes[0].data);
var num3=num1 * num2;
cells[3].childNodes[0].value = num3.toString();
}
}
}
}
</script>
I can get the values of column1 and column2, but the value in last column is not getting populated. The last line does not seem to work.
cells[3].childNodes[0].value = num3.toString();
What should I change?
The below html code is part of my .jsp file.
<form action="BuyServlet">
<table border="1" cellpadding="5" id="mytable">
<tr>
<th>Stock Name</th>
<th>Buy Quantity</th>
<th>Market Price</th>
<th>Amount</th>
</tr>
<tr>
<td>Stock Name</td>
<td><input type="text" name="quantity" onblur="populate()"></td>
<td>122</td>
<td><input type="text" name="amount">
</d>
</tr>
<tr>
<td>Stock Name</td>
<td><input type="text" name="quantity" onblur="populate()"></td>
<td>111</td>
<td><input type="text" name="amount"></td>
</tr>
</table>
</form>
Basically you are getting the value from the text box (best quantity) and you are using data to get the value of the Market price(better use innerText)
try this (Replace with your code inside loop)
var num1 =parseFloat(cells[1].childNodes[0].value);
var num2 =parseFloat(cells[2].innerText);
var num3=num1 * num2;
cells[3].innerText = num3.toString();
Your code is in need of improvement. In your table, you should have a thead and a tbody sections. This will make it more accessible and easier to ingore the heading row.
<table border="1" cellpadding="5" id="mytable">
<thead>
<tr>
<th>Stock Name</th>
<th>Buy Quantity</th>
<th>Market Price</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>Stock Name</td>
<td><input type="text" name="quantity"></td>
<td>122</td>
<td><input type="text" name="amount"></td>
</tr>
<tr>
<td>Stock Name</td>
<td><input type="text" name="quantity"></td>
<td>111</td>
<td><input type="text" name="amount"></td>
</tr>
</tbody>
</table>
</form>
Now with the code, you should be adding the onblur with code, not hardcoded. You are looping through the cells, there is no reason for that. Also there is no need to loop every row when the table is changed. Just calculate the one that changed! Using childNodes can be tricky because of whitespace differences in browsers. Run this code after the table has been rendered.
(function () {
var table = document.getElementById("mytable");
var tbody = table.getElementsByTagName("tbody")[0];
var rows = tbody.getElementsByTagName("tr");​​​​​​​​​
function populateRow (index, addBlurEvent) {
var row = rows[index];
var cells = row.getElementsByTagName("td")
var textboxes = row.getElementsByTagName("input");
var amountTextbox = textboxes[0];
var totalTextbox = textboxes[1];
var costCell = cells[2];
var amount = amountTextbox.value.length>0 ? parseFloat(amountTextbox.value) : 0;
var cost = parseFloat(costCell.innerHTML);
var total = amount * cost;
totalTextbox.value = total;
if (addBlurEvent) {
amountTextbox.onblur = function () { populateRow(index, false); };
}
}
for (i=0;i<rows.length;i++) {
populateRow(i, true);
}
}());
The running fiddle of the above code
​
I think the error is due to spelling mistake - you have childnodes instead of childNodes on "cells[3].childnodes[0].value = num3.toString();"
Check this fiddle - http://jsfiddle.net/VwU7C/

Categories