Forum Moderators: open

Message Too Old, No Replies

Problem with JavaScript in Loop

         

SeanF

2:37 pm on Nov 22, 2020 (gmt 0)

5+ Year Member Top Contributors Of The Month



I am trying to create a form which will loop through multiple line items and calculate a price for each line item based on user input. the JavaScript is not reading the input values from the correct lines. I am sure I am misusing the $i variable... probably just not seeing the forest for the trees.

The input form is a table that loops through a number of rows:
$numRows = 2;
$i = 2;
for ($x = 1; $x <= $numRows; $x++) {
$i = $x;
echo"
<tr>
<td><input type='text' name='boat[$i]' /> </td>
<td><input type='text' name='boat_loa[$i]' id='boat_loa$i' placeholder='LOA $i' onkeyup='calcBoatCost()' /></td>
<td><input type='text' name='boat_beam[$i]' id='boat_beam$i' placeholder='Beam $i' onkeyup='calcBoatCost()' /></td>
<td>
<select name='product' id='unit_price$i' onchange='calcBoatCost()'>
<option value='2' >select one</option>
<option value='3' ><strong>Water Space Stern to</strong></option>
<option value='4' > <strong>Water Space Side to </strong></option>
</select>
</td>
<td><input type='text' name='boat_area[$i]' id='boatAreaResult$i' /></td>
<td><input type='text' name='boat_cost[$i]' id='boatCostResult$i' /></td>
</tr>
";
}

The "$i = 2;" and "$i = $x;" lines are so I can force "$i" to see what values are being sent. The value of "$i" in the "placeholder" shows the correct values.

The JavaScript looks like this:
echo "
<script type=\"text/javascript\" src=\"//code.jquery.com/jquery-1.10.2.js\"></script>
<script type=\"text/javascript\">
function calcBoatCost() {
var boat_loa = document.getElementById('boat_loa$i').value;
var boat_beam = document.getElementById('boat_beam$i').value;
var unit_price = document.getElementById('unit_price$i').value;
var boatArea = parseFloat(boat_loa) * parseFloat(boat_beam) ;
if (!isNaN(boatArea)) {
document.getElementById('boatAreaResult$i').value = boatArea;
}
var boatCost = boatArea * parseFloat(unit_price) ;
alert ('LOA: ' + boat_loa + ', Beam: ' + boat_beam + ', Area: ' + boatArea + ', Unit Cost: ' + unit_price + ', Cost: ' + boatCost);
if (!isNaN(boatCost)) {
document.getElementById('boatCostResult$i').value = boatCost;
}
}
</script>
";


When data is entered, the javascript calculates the correct values but regardless of which row values are entered in, the values being used in the JavaScript are always from the last row.

What am I doing wrong?

NickMNS

5:12 pm on Nov 22, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



The basic problem lies in your PHP loop. You are creating your form in the loop with a counter "i" and after adding a <script> block with the js code referencing the counter "i". But "i" is outside the loop so it's value is the last value in the loop. Therefore your JS always returns that last value.

The root problem is that the JS cannot use the counter "i", otherwise you would need to add as many js blocks as there are form references, which is not a good idea. Instead you should structure your table elements into a form and then call the js on a change to the form and then reference that instance of the form.

robzilla

5:27 pm on Nov 22, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



When your for() loop in PHP is done, $i will still have the value that it's last given. If you have 2 rows, $i will equal 2, with 10 rows it'll be 10.

So when you later declare your calcBoatCost() in Javascript, $i will echo as 2 (or 10), like so:

var boat_loa = document.getElementById('boat_loa2').value;

You can see this in the HTML source of the page.

As such, calcBoatCoast() is always going to return the values of the last row.

So it doesn't really make sense to use $i in your Javascript. Instead, you could pass $i to the calcBoatCost() from within the for() loop.

<select name='product' id='unit_price$i' onchange='calcBoatCost($i)'> 

And then grab that value in Javascript:

function calcBoatCost(rowNumber) {
var boat_loa = document.getElementById('boat_loa' + rowNumber).value;

Edit: what NickMNS (more succinctly) said ;-)

NickMNS

7:23 pm on Nov 22, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Here as an added bonus is demo to show the JS.
[jsfiddle.net...]

There is only one block of js code and two form elements, but there could be as many form elements as you like, it won't change anything. Also the counter "i" is not used except in the form id, but even that could be eliminated.

It's not my best work, I struggle a bit having to stick to ES5. There are so many improvements in ES6 that would make a problem like this much simpler, and the code more elegant. But I digress.

Some small caveats:
- You should likely include some kind of validation, your form inputs are text, so one could enter text and that would throw an error or give a bizarre result.
- Your placeholders don't show because they are replaced with the default values. Typically it is not a good idea to use placeholders as input descriptions. One should use labels that don't disappear when value are entered.
- I just used a basic math equation to display a result, you may want something more elaborate.

SeanF

10:28 pm on Nov 22, 2020 (gmt 0)

5+ Year Member Top Contributors Of The Month



Ah, right. As I said, can't see the forest for the trees. I think I've made this exact mistake before. "Those who cannot remember the past are condemned to repeat it"

Thank you for your assistance and for the examples.

SeanF

12:11 am on Nov 23, 2020 (gmt 0)

5+ Year Member Top Contributors Of The Month



Next Logical question, I guess is what's the best way to get the sum of areas and costs for all rows. I thought I could create a variable called "GrandTotaL" and use something like "grandTotal += boatCost" but this function is calculating on a single line. How would I get the sum of "boatCost" for all lines?

Thanks!