How to display output of JavaScript calculation in html form?

I created simple Javascript that calculates total based on two input variables (price, quantity). The script should take specified values in HTML form and show the calculated result in the “Total” field. The calculations should be updated on oninput event. How to accomplish this correctly, to pass values of input fields (price, quantity) to Javascript on oninput event and dynamically update result in “Total” field?

<!DOCTYPE html>
<html>
<body>

<form>
  <label for="price">Price:</label><br>
  <input type="number" id="price" name="price" value="0.0458" min="0" step="0.0001"><br><br>
  <label for="quantity">Quantity:</label><br>
  <input type="number" id="quantity" name="quantity" value="1" min="1"><br><br>
  <label for="total">Total:</label><br>
  <input type="text" id="total" name="total" value="" readonly><br><br>
  <input type="reset" value="Reset">
</form>
   
<script>
let B1 = 0.0458;
let B2  = 1;
let B3 = B2 * 10.702 * 1.02;
let B4 = B3 * B1;
let B5 = B3 * 0.00321;
let B6 = Math.round((B4 + B5) * 100) / 100;
let B7 = B3 * 0.0121219;
let B8 = 4.62;
let B9 = B7 + B8;
let B10 = B3 * 0.0038;
let B11 = B6 + B9 + B10;
let B12 = Math.floor((B11 * 1.21) * Math.pow(10, 2)) / Math.pow(10, 2);

element.addEventListener("input", setPrice);
    
    function setPrice() {
      document.getElementById("price").value = B1;
    }
    
    element.addEventListener("input", setQuantity);
    
    function setQuantity() {
      document.getElementById("quantity").value = B2;
    }
</script>

</body>
</html>

OK, lots of things going on here that need fixing. First of all, you should start by hooking up your event handlers correctly and that means defining what “element” is. If you set something like…

const element = document.forms[0]; // Assumes you only have the one form

Now “element” will represent your form and all event listeners on inputs of that form will be caught by the addEventListeners you have defined.

The next step is that when these listeners do fire, you are immediately setting them to the same value (B1 or B2). You need to instead read the current value in the inputs, then do the multiply and set it to total. I recommend that you do something variable definitions for them…

let price = document.getElementById(“price”);
let quantity = document.getElementById(“quantity”);

Once you have these, you can do this in your event listeners…

document.getElementById(“total”).value = price.value * quantity.value;

The trick here is that if someone enters input, the event listeners fire, read the values of the two fields, multiplies them together and puts the result into the total field.

Keep in mind that by setting your inputs to your static variables, each time the event triggers it is just dumping your static variable back into the input. Hence you see no change in values when you type into the inputs. :slight_smile:

1 Like

Thanks for the tips. Did you mean this should be as follows?

const element = document.forms\[0\];
element.addEventListener(“input”, setPrice);
let price = document.getElementById(“price”);
element.addEventListener(“input”, setQuantity);
let quantity = document.getElementById(“quantity”);
document.getElementById(“total”).value = price.value \* quantity.value;

Naming your inputs as you have done, does make things a little more convenient.

In the DOM your form has an elements property form.elements. If you open up a console log e.g. F12 in the browser you can inspect your form.

const form = document.querySelector('form');
const inputs = form.elements;

console.dir(form); // see the entire form
console.dir(inputs); // see the form.elements

With the above you should be able to see something like this.

With the above form.elements assigned to a variable of ‘inputs’ you can access the values like this

inputs.['total'].value = inputs['price'].value * inputs['quantity'].value

To make the code easier to read you can use destructuring to assign the elements to variables e.g.

const { price, quantity, total } = inputs;

then use

total.value = price.value * quantity.value;

This is one reason why naming your inputs I think is beneficial :slight_smile:

1 Like

To display a total on making changes to the inputs you can add an onChange event listener to the form.

const form = document.querySelector('form');
const inputs = form.elements;
const { price, quantity, total } = inputs;

// helper function to fix the number to a given
// number of decimal places. Default is 3
function toFixed(x, places = 3) {
    return Number.parseFloat(x).toFixed(places);
}

// given x and y displays the total
function updateTotal(x, y) {
    total.value = toFixed(x * y);
}

// A listener added to the form.
// When a change is made to the inputs the total is updated
form.addEventListener('change', (event) => {
    updateTotal(price.value, quantity.value);
})
1 Like

Thank you for help. The code works, but it calculates in a different way. :slight_smile:

The correct result for default values of price x quantity is: *0.0458 x 1 = 6.44 (because ‘total’ = B12) (*since my code calculates the total as a result of successive operations).

So it’s not 0,0458 x 1 = 0,0458

Here is what I mean, this show correct total:

HTML

<p id="total"></p>

Javascript

let B1 = 0.0458;
let B2  = 1;
let B3 = B2 * 10.702 * 1.02;
let B4 = B3 * B1;
let B5 = B3 * 0.00321;
let B6 = Math.round((B4 + B5) * 100) / 100;
let B7 = B3 * 0.0121219;
let B8 = 4.62;
let B9 = B7 + B8;
let B10 = B3 * 0.0038;
let B11 = B6 + B9 + B10;
let B12 = Math.floor((B11 * 1.21) * Math.pow(10, 2)) / Math.pow(10, 2);
document.getElementById("total").innerHTML = B12;

I tried code in this editor.

HTML

<form>
  <label for="price">Price:</label><br>
  <input type="number" id="price" name="price" value="0.0458" min="0" step="0.0001"><br><br>
  <label for="quantity">Quantity:</label><br>
  <input type="number" id="quantity" name="quantity" value="1" min="1"><br><br>
  <label for="total">Total:</label><br>
  <input type="text" id="total" name="total" value="" readonly><br><br>
  <input type="reset" value="Reset">
</form>

Javascript

let B1 = 0.0458;
let B2  = 1;
let B3 = B2 * 10.702 * 1.02;
let B4 = B3 * B1;
let B5 = B3 * 0.00321;
let B6 = Math.round((B4 + B5) * 100) / 100;
let B7 = B3 * 0.0121219;
let B8 = 4.62;
let B9 = B7 + B8;
let B10 = B3 * 0.0038;
let B11 = B6 + B9 + B10;
let B12 = Math.floor((B11 * 1.21) * Math.pow(10, 2)) / Math.pow(10, 2);

const form = document.querySelector('form');
const inputs = form.elements;
const { price, quantity, total } = inputs;
// helper function to fix the number to a given
// number of decimal places. Default is 3
function toFixed(x, places = 3) {
    return Number.parseFloat(x).toFixed(places);
}
// given x and y displays the total
function updateTotal(x, y) {
    total.value = toFixed(x * y);
}
// A listener added to the form.
// When a change is made to the inputs the total is updated
form.addEventListener('change', (event) => {
    updateTotal(price.value, quantity.value);
})

So the goal is to show B12 result in ‘total’ in the form.

And what happened?

You have all those assignment statements at the start of your code, but don’t seem to call them anywhere. So you need to edit that into your updateTotal() function as required.

Ok so the confusion here comes with your use of variable names, b1, b2 etc.

Can these be substituted for English language words e.g. price, quantity, shipping, tax etc.

My understanding is B1 is price, and B2 is quantity. Based on that I have made those substitutions and put it into a function.

I have also substituted the rounding calculations with helper functions. This starts to make the calculations more readable. Without knowing what the rest of the ‘b’ variables represent, it is difficult to do any more refactoring.

// A function to round a number to a specified number of decimal places
// The default number of decimal places is 2
// @returns the rounded number
function roundNumber (num, places = 2) {
    const factor = Math.pow(10, places);

    return Math.round(num * factor) / factor;
}

// A function to round a number down to a specified number of decimal places
// The default number of decimal places is 2
// @returns the rounded number
function roundNumberDown (num, places = 2) {
    const factor = Math.pow(10, places);

    return Math.floor(num * factor) / factor;
}

function calculateTotal (price, quantity) {
    const b3 = quantity * 10.702 * 1.02;
    const b4 = b3 * price;
    const b5 = b3 * 0.00321;
    const b6 = roundNumber(b4 + b5);
    const b7 = b3 * 0.0121219;
    const b8 = 4.62;
    const b9 = b7 + b8;
    const b10 = b3 * 0.0038;
    const b11 = b6 + b9 + b10;
    const total = roundNumberDown(b11 * 1.21);

    return total;
}

const form = document.querySelector('form');
const inputs = form.elements;
const { price, quantity, total } = inputs;

// a listener added to the form
// when a change is made to the inputs
// the total is updated
form.addEventListener('change', (event) => {
    total.value = calculateTotal(price.value, quantity.value);
});

Hopefully the calculations are correct.

edit: The first ones I would like to understand.

What is 10.702 and 1.02 in the initial calculation?

b8 is a fixed constant number by the looks of it, does it have an English name?

… What?

… What?

Someone’s been huffing AI too hard.

B6 = B4+B5;
B6 = (B3*p)+(B3* 0.00321)
B6 = (Q * 10.91604 * P) + (Q * 0.0350404884)
B6 = Q*((10.91604*P)+0.0350404884)

B9  = B3 * 0.0121219 + 4.62
B9 = Q * 10.91604 * 0.0121219 + 4.62
B9 = Q * 0.132323145276 + 4.62

B10 = Q * 10.91604  * 0.0038
B10 = Q * 0.041480952

B11 ~= B6 + B9 + B10
B11 ~= Q*((10.91604*P)+0.0350404884) + (Q * 0.132323145276 + 4.62) + (Q * 0.041480952)
B11 ~= Q*((10.91604*P)+0.0350404884+0.132323145276+0.041480952) + 4.62
B11 ~= Q*((10.91604*P)+0.208844585676)+4.62

(There’s some rounding factor in B11 because you round halfway through your calculation, for reasons passing understanding, that will only cause rounding errors in your final value.)

Though I suppose what we really should be doing is combining resources, considering I can see you’ve posted this same code to at least 2 other coding forums… waves to coot

Confused what is the issue @m_hutley?

I just substituted what the OP posted

<input type="number" id="price" name="price" value="0.0458" min="0" step="0.0001">
<input type="number" id="quantity" name="quantity" value="1" min="1">
let B1 = 0.0458; // presumably price
let B2  = 1; // presumably quantity
let B3 = B2 * 10.702 * 1.02;
let B4 = B3 * B1;
let B5 = B3 * 0.00321;
let B6 = Math.round((B4 + B5) * 100) / 100; // rounding to the hundred
let B7 = B3 * 0.0121219;
let B8 = 4.62;
let B9 = B7 + B8;
let B10 = B3 * 0.0038;
let B11 = B6 + B9 + B10;
let B12 = Math.floor((B11 * 1.21) * Math.pow(10, 2)) / Math.pow(10, 2); // rounding down to the hundred
// A function to round a number down to a specified number of decimal places
// The default number of decimal places is 2
// @returns the rounded number
function roundNumberDown (num, places = 2) {
    const factor = Math.pow(10, places);

    return Math.floor(num * factor) / factor;
}

Again what is the issue with this helper function?

To me this
roundNumberDown(b11 * 1.21);

is clearer than this
Math.floor((B11 * 1.21) * Math.pow(10, 2)) / Math.pow(10, 2);

I wasn’t aware that this had been posted on other forums.

BTW I think I might have got crossed wires here :slight_smile:

Yes, apologies to rpg. The AI comment was that the original code looks like a BDD AI parse of the problem where it’s taken a set of requirement steps and iterated them literally, rather than combining the mathematical processes down to simplified forms, and my eyes didnt check which version i was quoting in my response. If the numerical values aren’t constant, they’d be input variables and we’d have more fields with names?

That said, i’m not generally a big fan of helper functions that are already extant and writable in quite concise format. roundNumber(number,places) = parseFloat(number.toFixed(places))

1 Like

Point taken :slight_smile:

Hi, I took all the designations from the Excel table, they are just cell numbers. I created calculations based on a monthly gas bill, that includes several components: actual monthly consumption, fixed costs for distribution, variable costs for consumption, taxes and service charges, vat. So in general, there are 3 main components: B2 - gas consumed volume(m3), B1 - price per kWh, B3 - gas energy in kWh, so

B1 - price per kWh
B2 - consumed volume (m3)
B3 - gas energy in kWh

B3 = B2 * 10.702 * 1.02; (gas volume * calorific value * static pressure coefficient)
The rest units are various increasing coefficients (fixed or relative) used to increase the final price of gas for the consumer. I used Math.Round, Math.floor just to match the calculation result to the bill digits.
B1 - price per kWh
B2 - gas volume, m3
B3 = B2 * 10.702 * 1.02; - gas energy in kWh
B4 = B3 * B1; - gas energy cost
B5 = B3 * 0.00321; - Transmission system services fee
B6 = Math.round((B4 + B5) * 100) / 100; - gas total cost
B7 = B3 * 0.0121219 - Distribution service fee
B8 = 4.62 - Distribution service - fixed part fee
B9 = B7 + B8 - Distribution system services total
B10 = B3 * 0.0038 - excise tax
B11 - total
B12 - total with vat

isnt the calorific value of natural gas closer to 40?

EDit: oh youve reduced THAT part of the equation. i see…

“Right, that was a vague statement Marc. How about we wake up a little bit, and try with some explanation.” “It’s 4:30 in the morning. Go away.” “No. We’re both inside this head, so shut up and move arms.”

Anyway.

(m^3) * (....) * (unitless)
What Marc was rambling about: 10.702 is a preconverted figure of kWh, from the calorific value of gas (measured in MJ/m^3), and the conversion factor of MJ/kWH, 3.6. Just amusing that we chose not to simplify anything else but that.

(m^3) * (MJ/m^3) * unitless
---------------------------
        (MJ/kWh)

Also, based on that description, you’re rounding in the wrong place, surely.

If B9 and B10 are separate calculated fees, surely you need to round B9 and B10 prior to summing them in B11 (the same as you did to B6), not rounding the whole figure.
EDIT: Actually based on your description, shouldnt you round B7 before adding it in B9 also?
Mathematically, adding precision terms cannot add a degree of precision. You can never add ###.## and ###.## and end up with ###.###

1 Like

Yes, I probably rounded not in the right place, the goal was just to end up with the same final total values ​​that were in the actual bill.

So let me see if i’ve got this right.

You have 4 monetary values in consideration:

Your cost of actual gas; This is effectively Quantity * Price, but with conversions muddled in because why would we tell you the kWh value, while charging you per kWh.

Your cost of transmission, which can be defined in terms of Quantity * FixedRate(1).

Your cost of Distribution (which is somehow different than transmission because… go go fees), which can be defined as Quantity * FixedRate(2) + FlatFee.

Then there’s a Tax ONLY on the volume of gas (Quantity * FixedRate(3)). But we didnt count this as part of the gas cost.

Then there’s VAT on the whole thing, and not assessed individually for the fees and the gas.

B11 = 
B6 + B9 + B10
(B4+B5) + (B7 + F) + (B3 * D)
(B3*P)+(B3*V) + (B3 * T + F) + (B3 * D)

If you want to evaluate each as independent monetary values, you stop there, round each term in parenthesis, and then do the adding.

//Bind our change listener to everything but the reset button.
document.querySelectorAll("input:not([type='reset'])").forEach(x => x.addEventListener("change",findTotal));
//Run the initial calculation, to fill the Total box with our default values for price and quantity.
findTotal();

function findTotal() {
let price = document.getElementById("price").value;
let quantity = document.getElementById("quantity").value;
let volume = quantity * 10.91604;
// [P,V,T,D]
let pricemults = [price,0.00321,0.121219,0.0038]
let flatfee = 4.62;
let vat = 1.21
//Now Calculate.
//If you dont care about pre-tax, combine these lines....
let totalpretax = pricemults.reduce((a,c) => a + findCost(c,volume),0) + flatfee
let vattotal = totalpretax * vat;
//Return rounded string.
document.getElementById("total").value = vattotal.toFixed(2);
}

//Look, i'll even do a helper function ;P
function findCost(taxval,volume) {
  return parseFloat((taxval * volume).toFixed(2));
}

EDIT: Dont need the last parseFloat, Marc… the text box wants a string.
EDIT2: Think i mixed my letters up in the mathematical breakdown. But the principle holds. There are some values [Price,Transmission,Distribution,Excise], that all get multiplied to Volume, rounded, and then summed.

1 Like

So what should the full code look like?

That response tells me everything i need to know. Best of luck to you

You do not need to hardcode the values in variables like B1 and B2. Instead, read the values directly from the inputs on each input event and update the total.

In similar cases, I usually attach one function to both fields, get their current values, calculate, and then set the result:

function updateTotal() {
  const price = parseFloat(document.getElementById("price").value) || 0;
  const qty = parseFloat(document.getElementById("quantity").value) || 0;

  const total = price \* qty; // plus your extra calculations
  document.getElementById("total").value = total.toFixed(2);
}

price.addEventListener("input", updateTotal);
quantity.addEventListener("input", updateTotal);

This way it recalculates instantly whenever the user changes a value, and you’re always using the actual form data.