Dynamically Generating HTML - javascript

I'm an absolute beginner, been self-studying for two weeks in preparation for a bootcamp coming up in a month. In an effort to improve my skills and "learn to think like a developer", I've taken on a project which attempts to solve a real-world problem.
My wife runs a business requiring her to generate invoices for her clients. I am attempting to use HTML and JavaScript to build a web-based application which enables her to quickly create custom invoices instead of writing them out manually each time.
In the current version, a prompt requests a number from her. This number generates an equal number of three-column rows in HTML. This enables her to customize the invoice with the exact number of fields she needs.
Column 1 contains the product name. In the same row, column 2 contains the unit count, while column 3 contains the total cost of that product, which is the base price multiplied by the unit count. I want this calculation to occur upon a button click. But, I'm stuck.
In order to perform the math, I need to be able to get the integers from each row and column and pass them into a function. But because each row and column was generated automatically, they have no unique attributes and cannot thereby be identified. I am able to perform the math down each column, but not across each row.
As I'm so new, having only learned from a couple introductory Codecademy courses and some YouTube videos, I don't know how to evaluate whether I'm approaching the project entirely wrong, or if there's some trick I'm missing, or if there's just something haven't learned yet. If someone with some expertise could nudge me in the right direction, I'd really appreciate it!
I've attached all the code to this post. Apologies if it's a horrible mess. Go easy on me, I'm a beginner!
const invDate = Date();
var field = "<label for='Item'>Item:</label> <input type=text name='item'>" +
"<label for='qty'> Qty: </label> <input type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input type='number' name='price'> <br><br>";
document.getElementById('newInvoice').onclick = function(){
let invoicedName = prompt('Who is this invoice made out to?', 'Enter a name');
let productFields = Number(prompt('How many product names on this invoice?', 'Please enter a number'));
let fields = ''
let dynHtml = ''
if (invoicedName != null && productFields != null) {
for (let i = 1; i <= productFields; i++) {
fields += field };
} else { alert('Please enter valid inputs.');
};
dynHtml = "<center><img src ='logo.jpg'><br></br>" +
"<h1>INVOICE</h1>" +
"<p>Prepared for: <b>" + invoicedName + "</b>, on" +
"<p>" + invDate + "</p><br></br>" +
fields + "<br></br>" +
"<button id ='calculate'>Calculate</button></center>";
document.write(dynHtml);
document.getElementById('calculate').onclick = function getQtyFields() {
let qtyInputs = document.getElementsByName('qty'),
resultQty = 0;
for ( let j = 0; j < qtyInputs.length; j++ ) {
if ( qtyInputs[j].id.indexOf('qty') == 0 ) {
let num = parseFloat( qtyInputs[j].value );
if( !isNaN(num) ) resultQty += num;
}
}
let priceInputs = document.getElementsByName('price'),
resultPrice = 0;
for( let k = 0; k < priceInputs.length; k++ ) {
if( priceInputs[k].id.indexOf('price') == 0 ) {
let num = parseFloat( priceInputs[k].value );
if( !isNaN(num) ) resultPrice += num;
}
}
alert(resultQty); alert(resultPrice)
}
}
Here's Screenshot,

As you said, your main issue is finding a way to give every input field within their own row their own unique identifier. This way you can calaulate the price for each row and insert it into the price field.
You first have to start with the fields:
var field = "<label for='Item'>Item:</label> <input type=text name='item'>" +
"<label for='qty'> Qty: </label> <input type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input type='number' name='price'> <br><br>";
document.getElementById('newInvoice').onclick = function(){
...
for (let i = 1; i <= productFields; i++) {
fields += field };
...
};
Every group needs its own identifier. That way you can later refer to every input in each row to calculate the sub total price. The class attribute is something you can assign to multiple elements to refer to them later. This class can be anything as long as it doesn't conflict with the class for any other row. You can use the i of the loop as your identifier, since it changes with every loop.
for (let i = 1; i <= productFields; i++) {
var field = "<label for='Item'>Item:</label> <input class='row-" + i + "' type=text name='item'>" +
"<label for='qty'> Qty: </label> <input class='row-" + i + "' type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input class='row-" + i + "' type='number' name='price'> <br><br>";
fields += field
};
This will add the class row-{i} to every field in every row. Better yet, you can refactor this into its own function
function generateFields(i) {
return "<label for='Item'>Item:</label> <input class='row-" + i + "' type=text name='item'>" +
"<label for='qty'> Qty: </label> <input class='row-" + i + "' type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input class='row-" + i + "' type='number' name='price'> <br><br>";
}
for (let i = 1; i <= productFields; i++) {
fields += generateFields(i);
};
You get something similar to the following html
<label for='Item'>Item:</label> <input type=text class='row-1' name='item'>
<label for='qty'> Qty: </label> <input type='number' class='row-1' name='qty'>
<label for='Price'> Price ($):</label> <input type='number' class='row-1' name='price'> <br><br>
<label for='Item'>Item:</label> <input type=text class='row-2' name='item'>
<label for='qty'> Qty: </label> <input type='number' class='row-2' name='qty'>
<label for='Price'> Price ($):</label> <input type='number' class='row-2' name='price'> <br><br>
<label for='Item'>Item:</label> <input type=text class='row-3' name='item'>
<label for='qty'> Qty: </label> <input type='number' class='row-3' name='qty'>
<label for='Price'> Price ($):</label> <input type='number' class='row-3' name='price'> <br><br>
Now in your calculate function, you can refer to these rows and calculate their price. Here you can loop over the 'item' input fields.
document.getElementById('calculate').onclick = function getQtyFields() {
let itemInputs= document.getElementsByName('item')
for(let i = 0; i < itemInputs.length; i++){
const identifier = itemInputs[i].className // get the class name of every item ex. 'row-1'
const row = document.getElementsByClassName(identifier);
}
...
}
row is now an HTMLCollection which contains the item, qty, and price for each row. With HTMLCollections you can select any of its elements by its name using .namedItem(<name>)
for(let i = 0; i < itemInputs.length; i++){
const identifier = itemInputs[i].className // get the class name of every item ex. 'row-1'
const row = document.getElementsByClassName(identifier); // ex. 'row-1'
const qty = row.namedItem('qty').value;
const basePrice = [YOUR BASE PRICE];
const itemSubTotal = basePrice * qty; // heres is your item sub total!
}
You can now insert the price price into the input field for that row.
for(let i = 0; i < itemInputs.length; i++){
const identifier = itemInputs[i].className // get the class name of every item ex. 'row-1'
const row = document.getElementsByClassName(identifier); // ex. 'row-1'
const qty = row.namedItem('qty').value;
const basePrice = [YOUR BASE PRICE];
const itemSubTotal = basePrice * qty; // heres is your item sub total!
const price = row.namedItem('price');
price.value = itemSubTotal; // insert subtotal inside the price field
}
Now once you click on calculate, you should see the alerts for total price and total quantity and should see the price input fields filled with the calculated price.
This solution is very basic and shouldn't introduce any new concepts you don't know. Later on in your bootcamp, you will learn will about design patterns and frameworks that can make these kind of projects easier to manage and scale.
Hit me up if run into any problems or have any other questions!

Related

How to increment an array with each dynamically created table row in JavaScript

I'm dynamically creating rows in a table with 3 columns: Item, Description & QTY. How do I increment an array with each dynamically created table row?
Here's the results I'm getting when submitting the form below containing two rows with the following values.
The first row values are: Item1, Description1, Qty1
and the second row values are: Item2, Description2, Qty2
1=Item1&1=Description1&1=QTY1&1=Item2&1=Description2&1=QTY2
I would like to return the following instead:
1=Item1&1=Description1&1=QTY1&2=Item2&2=Description2&2=QTY2
https://jsfiddle.net/rimshot609/zgdvxo83/4/
<form name="SiteForm" id="SiteForm" method="post" action="mailto:test#test.com">
<div class="line-item">
<fieldset>
<table id="textbox" border="0">
<tbody>
<tr>
<td>
</td>
</tr>
</tbody>
</table>
<input type="itembutton" onclick="createTableRows()" value="Add Item" />
</fieldset>
</div>
<input type="submit" name="subform" value="Submit Form" />
<script>
function createTableRows() {
var someText = 'Item, Name, Qty,'
var table = document.getElementById("textbox");
var rowlen = table.rows.length;
var row = table.insertRow(rowlen);
row.id = rowlen;
for (i = 0; i < 2; i++) {
arr = ['1']; //I need this number to increase by 1 with each table row created.
var x = row.insertCell(i)
if (i == 1) {
x.innerHTML = "<input type='button' onclick='removeCell(" + row.id + ")' value=Delete>"
} else {
x.innerHTML = "<input type='textbox' placeholder='Item Number' name='" + arr[i] + "' required='required'><input type='textbox' placeholder='Description' name='" + arr[i] + "' required='required'><input type='textbox' placeholder='QTY' name='" + arr[i] + "' required='required'>"
}
}
}
function removeCell(rowid) {
var table = document.getElementById(rowid).remove();
}
</script>
</form>
Without deletion, it's very simple. Just replace this line:
arr = ['1']; //I need this number to increase by 1 with each table row created.
With this:
arr = [row.id]; //I need this number to increase by 1 with each table row created.
row.id is always set to table.rows.length.
But when you introduce Deletion into the equation things get more complicated. Each time you delete a row you'll want to either change the value for the existing rows, or use another value that you increment differently.
The first solution feels quite elegant, but with the way this has been set up would be a little clunky to implement. The other would require something like:
let highestValue = 0;
function createTableRows() {
highestValue++;
var someText = 'Item, Name, Qty,'
var table = document.getElementById("textbox");
var rowlen = table.rows.length;
var row = table.insertRow(rowlen);
row.id = highestValue;
The problem is that you'll have gaps. If you have rows 1, 2 and 3 then delete 2, the results will jump from 1 to 3.

Unable to generate a multiplication table with user input in JavaScript

I have a page which prompts the user to enter a positive integer from 1 to 9, then the javascript code will generate a multiplication table from the input value all the way to 9. I am getting an error in which I cannot retrieve the value and do a multiplication with it.
function timesTable()
{
var values = document.getElementById('value1');
var showTables = '';
for (var i=1; i<9; i++) {
showTables += values + " x " + i +" = "+ values*i + "\n";
}
var p_tables = document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="text" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>
Expected result:
You have to take the value of the element not the element itself
var values = document.getElementById('value1').value;
function timesTable()
{
var values = document.getElementById('value1').value;
var showTables = '';
for (var i=1; i<9; i++) {
showTables += values + " x " + i +" = "+ values*i + "<br>";
}
var p_tables = document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="text" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>
You are trying to multiply the element itself. What you actually want is the value.
function timesTable()
{
var values = document.getElementById('value1').value;
var showTables = '';
for (var i=1; i<9; i++) {
showTables += values + " x " + i +" = "+ values*i + "\n";
}
var p_tables = document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="text" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>
the javascript line in which you are trying to find value, is wrong as it will return the whole DOM and it's attributes and property.
You just have to find it's value, replace you line
var values = document.getElementById('value1');
with
var values = document.getElementById('value1').value;
This does what you want.
Note that if the user enters something unexpected, it may still fail. You can use an input of type="number" to require an integer (at least in some browsers.)
const userValue = document.getElementById("value1").value;
const p_tables = document.getElementById("tables");
let outputHtml = "";
for(let i = 1; i < 10; i++){
outputHtml += userValue + " x " + i + " = " + userValue * i + "<br/>";
}
p_tables.innerHTML = outputHtml;
you are using input field as text for table generation its better to use Number as input type and to get the value of input field you have to use value function as used in above code and for line break use
<\br>(please ignore '\').
function timesTable()
{
var values = document.getElementById('value1').value;
var showTables = '';
for (var i=1; i<=9; i++) {
showTables += values + " x " + i +" = "+ values*i + "<br>";
}
document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="Number" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>

How to add or delete fields depending on value of another field

I have form:
<div class="fieldWrapper">
<label for="id_rooms">Rooms:</label>
<input id="id_rooms" type="number" name="rooms" min="1">
</div>
<div class="extrafieldWrapper">
</div>
Depending on number of rooms i want to add or delete new fields "adult" and "children". For example: if value of field "room" will be 2, it should generate two couples another fields for each room 'adult' and 'children', but if change value from 2 on 1, it should delete one couple of fields. And when change the value of 'room' field from 2 to 3, it should add one couple of fields. I saw many examples how to do this on java-script, so i try to write the script by my self, but it doesn't work correctly. Depending on value of field 'room' it only add new couple of fields.
$(function() {
var newFields = $('');
$('#id_rooms').bind('blur keyup change', function() {
var n = this.value || 0;
if (n + 1) {
if (n > newFields.length) {
addFields(n);
} else {
removeFields(n);
}
}
});
function addFields(n) {
for (form_num = newFields.length; form_num < n; form_num++) {
$("input[id='id_form-TOTAL_FORMS']").attr('value', form_num + 1);
$(".extrafieldWrapper").append("<br/><label for='id_form-" + form_num + "-adult'>Adult:</label> <input id='id_form-" + form_num + "-adult' type='number' name='form-" + form_num + "-adult'/> <label for='id_form-" + form_num + "-children'>Children:</label> <input id='id_form-" + form_num + "-children' type='number' name='form-" + form_num + "-children'/> ");
}
}
function removeFields(n) {
$('.extrafieldWrapper').html('');
}
});
I am newbie in java-script, can you tell me what i'm doing wrong. Thanks a lot.
It's a bit unclear what behavior exactly you wanted. Here I tried to represent it with code:
$(function () {
$('#id_rooms').bind('blur keyup change', function () {
var n = $('#id_rooms').val() || 0;
$("input#id_form-TOTAL_FORMS]").attr('value', n);
$(".extrafieldWrapper").empty();
for (var i = 0; i < n; i++) {
$(".extrafieldWrapper").append("<br/><label for='id_form-" + i + "-adult'>Adult:</label> <input id='id_form-" + i + "-adult' type='number' name='form-" + i + "-adult'/> <label for='id_form-" + i + "-children'>Children:</label> <input id='id_form-" + i + "-children' type='number' name='form-" + i + "-children'/>");
}
});
});
Anyway, you don't need two functions for adding and removing fields. You only need to rewrite the whole content of your div in one cycle. Or, if you prefer two functions approach, then you need to gather all the tags describing one pair of fields you add/remove under one div or span and give each of such groups an unique id by which you would address it.

Create arrays from input in a cycle JavaScript

I am currently saving user input within an accordion into arrays.
My Accordion is dynamic and on click, another accordion column with input fields is created:
var i = 1 ;
function AddPanel()
{
$('.accord').append('<h3 style="background:none;background-color:#C8C8C8 ;">Job Nr.'+i+'</h3>'
+'<div style="background:none;background-color:#E0E0E0;">'
+'<div>'
+'<form name="myForm">'
+'<table class="wrapper">'
+'<tr>'
+'<td style="text-align: left">First Digit:'
+'<div> <input type="text" name="Job['+i+'][0]" /></td>'
+'<td style="text-align: left">System:'
+'<div> <input type="text" name="Job['+i+'][1]" /></td>'
+'<td style="text-align: left">SAP Modul:'
+'<div> <input type="text" name="Job['+i+'][2]" /></td>'
+'</tr>'
+'<tr>'
+'<td style="text-align: left">Country:'
+'<div> <input type="text" name="Job['+i+'][3]" /></td>'
+'<td style="text-align: left">Duration:'
+'<div> <input type="text" name="Job['+i+'][4]" /></td>'
+'<td style="text-align: left">Step Number:'
+'<div> <input type="text" name="Job['+i+'][5]" /></td>'
+'</tr>'
+'<tr>'
+'<td style="text-align: left">Optional Text:'
+'<div>'
+'<textarea align="left" name="Job['+i+'][6]" cols="20" rows="2"></textarea>'
+'</div>'
+'</td>'
+'</tr>'
+'</table>'
+'</form>'
+'</div>'
+'</div>')
$('.accord').accordion("refresh");
i++;
}
Theoretically the user is able to dynamically make hundreds of inputs into a 2d Array.
My question now is: How would I be able to store and later on retrieve all inputs of the 2d array within a cylce?
I tried it lie It was suggested by tborychowski: `
var form = document.getElementsByName('myForm');
field = form.elements['Job[0][0]'];
formData = [], i = 0, j = 0;
while (field) {
formData[i] = [];
j = 0;
while (field) {
formData[i].push(field.value);
field = form.elements['Job[' + i + '][' + (++j) + ']'];
}
field = form.elements['Job[' + (++i) + '][0]'];
}
console.dir(formData);
`
I tried this in lots of different ways and googled for hours but I can not get it to work.
Sorry, I am a real beginner with this.
Thanks in advance!
I've created a demo (an example) of what you can do.
The basic idea (if I understand you correctly) is to name the form fields using loop indexes, like this:
<input type="text" name="Job[0][0]"/>
<input type="text" name="Job[0][1]"/>
So the first digit would be the group/set index, and the second - field index within that group/set.
Than - you just need to loop through these fields and no matter how many sets or fields in a set there is - you can gather all the values in an array, e.g.:
var form = document.getElementById('myForm'),
field = form.elements['Job[0][0]'],
formData = [], i = 0, j = 0;
while (field) {
formData[i] = [];
j = 0;
while (field) {
formData[i].push(field.value);
field = form.elements['Job[' + i + '][' + (++j) + ']'];
}
field = form.elements['Job[' + (++i) + '][0]'];
}
console.dir(formData);
I didn't use jquery here (with jquery it could be even easier).
Is this what you are looking for?
DEMO
depending on your scope, you could do something like this:
var a0 = 6, a1 = 5, a2 = 4, a3 = 2;
for (var i = 0; i < 4; i++) {
console.log(window['a' + i]);
}
so - if window is the scope your variables are in, you can use it like in the example above.
Otherwise - if that's not possible - you could create an array of values instead of separate variables, like so:
'<input type="text" name="Job['+i+'][]" />'
If you choose this approach - what I would do is add an index class (to make it easier for jquery, to the wrapper element (that encompasses all inputs of the same index), e.g.:
'<table class="inputs' + i + '">'+
---- inputs go here ----
'</table>'+
then loop through them and get the values, like so:
var jobs = [], idx = 0, inputs = $('.inputs' + idx);
while (inputs.length) {
inputs.find('input[name^=Job]').function(index, inputField) {
jobs[idx][].push($(inputField).val());
});
idx++;
inputs = $('.inputs' + idx);
}

Javascript - Array emptying value at close of function

I have a function which calculates a total quote for an order, which then alerts the output to the user. I also want the total quote to be stored in an array so a separate function can be called which will display all the values in the array (displaying all the quotes since the page was loaded). From what i can work out the array loses the value pushed in by the function as the function ends, and I have played around with the scope of the array to no joy and would appreciate a nudge in the right direction.
<form>
<table id="kit" cellpadding="2" cellspacing="5">
<th colspan="2" align="center"><h3>Purchase Shirts (Coming Soon)</h3></th>
<tr><td class="titles">Size</td>
<td class="titles">Qty</td></tr>
<tr><td>Small (£10)</td>
<td><input type="text" size="3" maxlength="5" name="small" /></td>
<tr><td>Medium (£12)</td>
<td><input type="text" size="3" maxlength="5" name="medium" /></td>
<tr><td>Large (£15)</td>
<td><input type="text" size="3" maxlength="5" name="large" /></td>
<tr><td>X-Large (£20)</td>
<td><input type="text" size="3" maxlength="5" name="xlarge" /></td>
<tr><td colspan="2" align="center">
<input class="submit" type="submit" onClick="return calculateShirts(this)" value="Get Quote" /></td>
</tr>
</table>
</form>
JavaScript------------
var totalQuotes = [1,2]; //Initialise the global array with example values
function calculateShirts(form) //Function to calculate shirt 'quote'
{
//Assign Prices for Each Shirt Size
var sml = 10;
var med = 12;
var lge = 15;
var xl = 20;
//Save the user inputs as variables
var smlQu = form.small.value;
var medQu = form.medium.value;
var lgeQu = form.large.value;
var xlQu = form.xlarge.value;
//Multiply the Price by the User Input and save as variable
var smlQuote = (sml * smlQu);
var medQuote = (med * medQu);
var lgeQuote = (lge * lgeQu);
var xlQuote = (xl * xlQu);
//Add the calculated values together to get the total price
var finalQuote = (smlQuote + medQuote + lgeQuote + xlQuote);
//Create an array containing the quotes
var arrayQuote = [smlQuote, medQuote, lgeQuote, xlQuote, finalQuote];
//Variable containing the formatted output of quotes
var output = "Your Kit Quote \n\n Small - £" + arrayQuote[0] + "\n" + "Medium - £" + quoteArray[1] + "\n" + "Large - £" + quoteArray[2] + "\n" + "X-Large - £" + quoteArray[3] + "\n\n" + "Total - £" + quoteArray[4];
//Display the output variable in a popup box
alert(output);
totalQuotes.push(finalQuote);
alert(totalQuotes); //This alert does show the calculated value
return false;
}
function printQuotes() //Function called on to display array values
{
for (i in totalQuotes) {
alert(totalQuotes[i]);
//The calculated value is no longer in the array
}
}
This works fine for me. There is some syntax error in there,
var output = "Your Kit Quote \n\n Small - £" + arrayQuote[0] + "\n" + "Medium - £" + quoteArray[1] + "\n" + "Large - £" + quoteArray[2] + "\n" + "X-Large - £" + quoteArray[3] + "\n\n" + "Total - £" + quoteArray[4];
You start by referencing arrayQuote then change to quoteArray, which doesn't exist. Not sure if this is just a typo when posting the question on here.
Given these values that I hardcoded:
var smlQu = 2;
var medQu = 1;
var lgeQu = 3;
var xlQu = 5;
alert(totalQuotes); // returns 1,2,177
printQuotes(); // returns alerts with 1 then 2 then 177
to stop the form refreshing add this line to the bottom of calculateShirts():
return false;
and change the form onsubmit from:
onsubmit="calculateShirts(this)" to onsubmit="return calculateShirts(this)"
if you still want to run the print method just call it before the return false.

Categories

Resources