New to JavaScript.
I have a form with three fields, quantity, amount (hidden), sum.
The user enters the quantity they would like to buy and its multiplied with amount and shows the total in sum. If the user change the amount the sum changes. What I cant solve is how to save the sum in the field, when the user use checkout the sum fields ends up empty.
The script:
function getElementByName(name) {
const elements = document.getElementsByName(name)
if (elements.length !== 1) {
throw new Error('multiple elements found with name ' + name)
}
return elements[0]
}
/**
* Calculate sum
*/
function calculateSum() {
const num1 = getElementByName('quantity').value;
const num2 = getElementByName('amount').value;
getElementByName("sum").value = num1 * num2;
}
/**
* Listen to events
*/
function listenToEvents() {
const elem = getElementByName('quantity')
elem.addEventListener('keyup', () => {
calculateSum()
})
}
listenToEvents()
You do not need the event handler function inside a function. I will also suggest you to use input event instead of keyup. You can also attach the event on both quantity and amount.
Demo:
function getElementByName(name) {
const elements = document.getElementsByName(name)
if (elements.length !== 1) {
throw new Error('multiple elements found with name ' + name)
}
return elements[0]
}
/**
* Calculate sum
*/
function calculateSum() {
const num1 = getElementByName('quantity').value;
const num2 = getElementByName('amount').value;
getElementByName("sum").value = num1 * num2;
}
/**
* Listen to events
*/
const elems = [getElementByName('quantity'),getElementByName('amount')]
elems.forEach(function(el){
el.addEventListener('input', () => {
calculateSum()
});
});
Quantity: <input type="number" name="quantity"/><br><br>
Amount: <input type="number" name="amount"/><br><br>
Sum: <input name="sum"/>
Related
JavaScript newbie here.
I am tasked with calculating a user's monthly payment by using a given equation. I am having trouble with getting the values from the user.
const loanAmount = document.getElementById('loan-amount');
const loanYears = document.getElementById('loan-years');
const loanRate = document.getElementById('loan-Rate');
const span = document.querySelector('span');
const form = document.getElementById("calc-form");
form.addEventListener("submit", function (e) {
e.preventDefault();
console.log('hello')
makeLogo();
});
function makeLogo(loanAmount, loanYears, loanRate) {
const principle = loanAmount.value
const n = loanYears.value * 12;
const i = loanRate.value / 12;
const monthylPayment = (principle* i)/1-(1+ i)** -(n);
span.innerText = monthylPayment;
}
This is what I have so far and am getting an error for the variables in the makeLogo function.
It's a good idea to separate your inputs, calculations and rendering into separate functions. try to keep functions as simple as possible.
You will need to re-evaluate your monthly cost calculator, but here is a working example which takes input, calculates and then renders into form fields.
document.getElementById("calc-form").addEventListener('submit', (e) => {
e.preventDefault();
var loanAmount = document.getElementById('loan-amount').value;
console.log(loanAmount);
var loanYears = document.getElementById('loan-years').value;
var loanRate = document.getElementById('loan-rate').value;
var monthlyPayment = makeLogo( loanAmount, loanYears, loanRate );
console.log(monthlyPayment);
// the monthly has now been calculated, simply put it where you'd like
var calculated = document.getElementById('calculated');
calculated.value = monthlyPayment;
var totalRepayment = document.getElementById('totalRepayment');
totalRepayment.value = monthlyPayment * ( loanYears * 12 );
} );
function makeLogo( principle, loanYears, loanRate) {
var n = loanYears * 12;
var i = loanRate / 12;
var result = ( principle * i) / 1 - ( 1 + i )**-( n );
return result;
}
<html>
<form action='submit' id ='calc-form'>
Loan Amount:<input id ='loan-amount'></input><BR/>
Loan Years:<input id='loan-years'></input><BR/>
Loan Rate:<input id='loan-rate'></input><BR/>
<input type='submit'>
</form>
<span id='span-output'>
Monthly Payment :<input id='calculated' readonly><BR/>
Total Re-Payment :<input id='totalRepayment' readonly>
</span>
</html>
The error you are seeing is likely because the makeLogo function is trying to access the value property of the loanAmount, loanYears, and loanRate variables. Still, they are DOM elements and not their values.
You can fix this by accessing the value property of the DOM elements before passing them to the function like so:
form.addEventListener("submit", function (e) {
e.preventDefault();
console.log('hello')
const principle = loanAmount.value;
const n = loanYears.value * 12;
const i = loanRate.value / 12;
makeLogo(principle, n, i);
});
function makeLogo(principle, n, i) {
const monthylPayment = (principle* i)/1-(1+ i)** -(n);
span.innerText = monthylPayment;
}
This way, the makeLogo function receives the values of the input fields as arguments and can perform the calculation correctly.
Also, make sure that you are getting the right input from the user by checking the value of each element by doing the following:
console.log(loanAmount.value,loanYears.value,loanRate.value)
and check if they are the values that you are expecting.
I would like to access variable from inside a function. The variable tip is innerText of different buttons (5, 6, 7...), but they are in %, so I converted them into numbers. However, the numbers are accessible only from inside the percentage function. When I try to call the function and log the variable, it shows NaN. I would like to use the tip for calculation in calc function always after clicking a respective button. How can I do that?
let tip = 0;
const billInput = document.querySelector(".bill__input");
const peopleInput = document.querySelector(".people__input");
const individualTip = document.querySelector(".conclusion__tip-person");
const individualTotal = document.querySelector(".conclusion__total-person");
const reset = document.querySelector(".conclusion__reset");
const buttons = document.querySelectorAll(".select-tip__button");
function percentage() {
tip = parseInt(this.innerText);
}
buttons.forEach((button) => {
button.addEventListener("click", percentage);
});
function calc() {
if (billInput !== "" && peopleInput === "") {
}
individualTip.textContent = (billInput.value / 100) * tip;
individualTotal.textContent =
"$" + (billInput.value / peopleInput.value).toFixed(2);
}
document.addEventListener("input", calc);
To make it little bit smaller:
I cant access numbers from variable tip, which innerText of buttons with different values (5%, 10%...). These numbers are converted from strings to numbers in the percentage function. I can access the correct tip values after clicking on buttons only if I log it directly inside the percentage function. I would like to use it outside the function, however.
let tip = 0;
function percentage() {
tip = parseInt(this.innerText);
}
buttons.forEach((button) => {
button.addEventListener("click", percentage);
});
you need change string to integer before you calculation
parseInt(peopleInput.value)
In the if (billInput !== "" && peopleInput === ""), you should return to not execute the reset of the function, Also the inputs values be as string format, you need to convert to number, you can use + operator.
let tip = 0;
const billInput = document.querySelector(".bill__input");
const peopleInput = document.querySelector(".people__input");
const individualTip = document.querySelector(".conclusion__tip-person");
const individualTotal = document.querySelector(".conclusion__total-person");
const reset = document.querySelector(".conclusion__reset");
const buttons = document.querySelectorAll(".select-tip__button");
function percentage() {
tip = parseInt(this.innerText);
}
buttons.forEach((button) => {
button.addEventListener("click", percentage);
});
function calc() {
if (billInput !== "" && peopleInput === "") {
// if there no value doesn't execute the rest of the function.
return
}
individualTip.textContent = (+billInput.value / 100) * tip;
individualTotal.textContent =
"$" + (+billInput.value / +peopleInput.value).toFixed(2);
}
document.addEventListener("input", calc);
So for anyone who would encounter similar problem, I solved this one. My variables and fucntions works properly. All I had to do was to put function calc() as the last line inside percentage() function. That did the trick. That's it.
Why is this helper function returning a value for subtotal and tax but NAN for fees and ordertotal?
OrderSummary.jsx
const taxRate = 0.0925;
const OrderSummary = ({ addCSS, classes, merchantId, merchantName, step, setStep }) => {
const context = useContext(ProductContext);
let items = Array.isArray(context.cart) ? context.cart : [];
if (step === 4) {
items = Array.isArray(context.order) ? context.order : [];
}
const subtotal = items.reduce((acc, cur) => {
return acc + cur.total * (cur.company_id === merchantId) * 1;
}, 0);
let tax = subtotal * taxRate;
tax = parseFloat(tax.toFixed(2));
let fee = subtotal * ApplicationFee * 0.01;
fee = parseFloat(fee.toFixed(2));
const total = subtotal + tax + fee;
<LineItem>
<S2>Subtotal</S2>
<S2>{formatCurrency(subtotal)}</S2>
</LineItem>
<LineItem>
<S2>Tax</S2>
<S2>{formatCurrency(tax)}</S2>
</LineItem>
<LineItem>
<S2>Fee</S2>
<S2>{formatCurrency(fee)}</S2>
</LineItem>
<Total>
<H5>Order Total</H5>
<H5>{formatCurrency(total)}</H5>
</Total>
helperFunctions.jsx
export const formatCurrency = (num) => {
if (typeof num !== 'number') return num
return new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD'}).format(num)
}
addTotals = () => {
let subTotal = 0;
this.state.cart.map((item) => (subTotal += item.total));
const tempFees = subTotal * ApplicationFee * 0.01;
const fees = parseFloat(tempFees.toFixed(2));
const total = subTotal + fees;
this.setState(() => {
return {
cartSubTotal: subTotal,
cartFees: fees,
cartTotal: total,
};
});
};
Image shows a Subtotal and Tax being calculated but NAN for Fee and Order Total
Believe it or not, but NaN (Not A Number) is a number:
typeof NaN !== 'number' // false
... so your first check just misses that case, and NaN (as value) gets nicely formatted to $NaN string by Intl.NumberFormat.
So you should consider checking how exactly fee is calculated in your case. This might fix your orderTotal, too (any calculation involving NaN results in NaN).
Another option is adding specific NaN check to your formatting function to prevent showing weird characters, like this:
if (Number.isNaN(num)) // ...
... but it's really hard to tell what should be displayed in this case.
using the code below, I've created a grid of buttons, 5x5, with random 1-25 numbers assigned to each button. They are to be clicked in numerical order, each's background turns red when clicked in the correct order. I can't use a global variable for this prompt. Without a global variable, I can't figure out how to increment the correctNumbers function which checks whether the right number is clicked each time. I think I'm missing something, a js function or something that would enable an incrementing variable declared within the incrementing function. I'm not looking for the whole explanation, just tips on functions i might not know about, and whether or not what i'm trying to do just isn't logicly possible.
<div id="numbers" class="hidden"></div>
<div id="youWon" class="hidden">You Won!</div>
The relevant JS:
... /**
* Gives the numbers a random order
* the "Fisher-Yates shuffle" found at: https://www.frankmitchell.org/2015/01/fisher-yates/
* #param {*} array
*/
const shuffle = (array) => {
let i = 0,
j = 0,
temp = null
for (i = array.length - 1; i > 0; i -= 1) {
j = Math.floor(Math.random() * (i + 1))
temp = array[i]
array[i] = array[j]
array[j] = temp
}
}
/**
* Generates an array of numbers 1-25
*/
const generateNums = () => {
document.getElementById("youWon").classList.toggle("hidden", "visible");
const numberArray = [];
for (let a = 1; a <= 25; a++) {
numberArray.push(a);
}
shuffle(numberArray);
let numEl = document.getElementById('numbers'); //write into html div id "numbers"
for (let b = 0; b <= 24; b++) { //loop to create button array
let newBtn = document.createElement('button'); //create buttons
newBtn.className = 'number'; //assign newBtns 'number' class
newBtn.innerText = numberArray[b]; //assign numbers to each button
numEl.appendChild(newBtn); //match with number elements in "numbers" array
newBtn.addEventListener("click", onNumberClick) //create function trigger
}
}
/**
* Creates a function to decide correct and incorrect clicks
* When a user clicks a number, if it is the next number in order, then it turns a different color for the remainder of the test
* If it is the wrong number, nothing happens
* #param {*} event
*/
const incrementNum = (correctNumber) => {
correctNumber++;
}
const onNumberClick = (event) => {
let correctNumber = 1; //start at 1
let numberVal = event.target; //apply it to clicks on the numbers
if (Number(numberVal.innerHTML) + 1 == incrementNum(correctNumber)) {
incrementNum(correctNumber);
numberVal.classList.add("red");
}
if (correctNumber == 26) {
document.getElementById("youWon").classList.toggle("visible"); //show win message if 25 is the last button and gets clicked
}
}
I would suggest that you count the number of elements in the DOM that have the class "red" and add 1... checking if the innerHTML is equal to that number to get the sequence right. So, instead of this:
if (Number(numberVal.innerHTML) + 1 == incrementNum(correctNumber)) {
incrementNum(correctNumber);
numberVal.classList.add("red");
}
You can have something like this:
if(Number(numberVal.innerHTML) == document.getElementsByClassName('red').length + 1) {
numberVal.classList.add("red");
}
Im trying to add a tax field. I have this working for whole numbers but i need to be able to enter ".5"
I have no clue haw to solve this problem maybe its because of the isNAN but i thought this would be ok here.
http://jsfiddle.net/thetylercox/eeMva/3/
My current code
$(document).ready(function() {
calculateSum();
$(".txt").keyup(function() {
$(".txt").each(function() {
calculateSum();
});
});
});
$("#tax").keyup(function() {
$('#total1').val(parseInt($(this).val()) * parseInt($('#subtotal').val()));
);
function calculateSum() {
var sum = 0;
$("#sum").val(sum.toFixed(2));
//iterate through each textboxes and add the values
$(".txt").each(function() {
//add only if the value is number
if (!isNaN(this.value) && this.value.length != 0) {
sum += parseFloat(this.value);
}
});
$("#sum").html(sum.toFixed(2));
var subtotal = document.getElementById("subtotal").value == "";
var subtotal = document.getElementById("subtotal").value = sum;
function getTax(tax) {
var taxFloat = parseFloat(tax)
if (isNaN(taxFloat)) {
return 1;
} else {
return taxFloat;
}
}
var total = getTax($('#tax').val()) * sum;
var total1 = document.getElementById("total1").value = total;
}
Thanks
Try this:
Put all code including functions inside your $(document).ready(function(){...}) structure.
Perform all calculations, including tax, inside calculateSum().
Use jQuery all through, in particular '$(...)' in preference to .getelementById(...).
Attach calculateSum as the 'keyup' handler for all the user-enterable fields.
Purge all sorts of junk from the code
It should look like this :
$(document).ready(function(){
function getTax() {
var taxFloat = parseFloat($("#tax").val());
return isNaN(taxFloat) ? 1 : taxFloat;
}
function calculateSum() {
var sum = 0;
$(".txt").each(function() {
if (this.value && !isNaN(this.value)) {
sum += parseFloat(this.value);
}
});
$("#subtotal").val(sum.toFixed(2));
$("#total1").val((getTax()*sum).toFixed(2));
}
$(".txt, #tax").keyup(calculateSum);
});
DEMO
You probably want to change the tax algorithm to something more logical. eg. for an entered value of 5(%), the multiplier should be 1.05 .
Taxes are based on percentages. Do the following changes:
$("#tax").keyup(function() {
//$('#total1').val(parseInt($(this).val()) * parseInt($('#subtotal').val()));
calculateSum();
});
...
function getTax(tax) {
var taxFloat = parseFloat(tax)
if (isNaN(taxFloat)) {
return 1;
} else {
// 1(subtotal) + ?(tax%) -> total = (1 + tax%) * subtotal
return 1 + (taxFloat/100);
}
}
var total = getTax($('#tax').val()) * sum;
// round to 2 decimal digits
total = Math.round(total * Math.pow(10, 2)) / Math.pow(10, 2);
var total1 = document.getElementById("total1").value = total;
So for 5% you enter 5 for 0.5% you enter .5
UPDATE: (Not really an update) As I see more answers coming in, I repeat the logical error for tax field usage. Taxes are percentages worldwide, Meaning that the problem was not only the keyup handling, but also the way tax value was used.
Found your error:
$("#tax").keyup(function() {
$('#total1').val(parseInt($(this).val()) * parseInt($('#subtotal').val()));
});
Here, you parse the tax with parseInt instead of parseFloat, as you do in the function getTax. parseInt(".5") gives NaN. Try to insert values in the .txt inputs after having inserted a tax, and it would work when invoking the calculateSum function.
I can't understand why you would use a different computation when pressing keys in the #tax field than on normal keypresses in other fields. Just use the same listener function, or, if you want to divide the functionality, invoke the function to display the tax both from the #tax keyhandler and the calculateSum function.
Also, there is no need to execute calculateSum() four times when one of the inputs is updated.
See better structured source code in updated fiddle. The maths to calculate taxes and totals was not corrected.