'onKeyUp event calculation not working on the following rows from php generated forms except the first one

    --------------------------------------------------
    |No|Style No|Item Desc|Qty|Unit Price|Total Price|
    --------------------------------------------------
    |1 |Style 1 |Item 1   |44 |3.00      |132.00     |
    |2 |Style 2 |Item 2   |3  |3.00      |9.00       |
    |3 |Style 3 |Item 3   |23 |34.00     |782.00     |
    |4 |Style 4 |Item 4   |56 |78.00     |4368.00    |
    |5 |Style 5 |Item 5   |34 |58.00     |1972.00    |
    --------------------------------------------------
    (Submit button)
    GRAND TOTAL: RM[_________ textbox] (Calculate Total button)

Alternatively this is the screenshot image: http://img715.imageshack.us/img715/8885/calcs.jpg

I have this table with a forms generated from the PHP script based on the table in the database. I've been pulling my hair out figuring how to make the following rows after the first one to work with the onkeyup event using JavaScript. The first row (in the red squared box) as the screenshot link above, seems to work well for example if I change the value of "Qty" in the first row to "1" it will automatically calculate and produce an output of "Total Price" to "3.00" but not on the following rows where it does nothing whenever i insert a new value. Strangely it does calculate each rows including in the "GRAND TOTAL" textbox when I click on the "Calculate Total" button

I cannot seems to figure out what the problem really is and how to fix it. Any help would be greatly appreciated.

Below is the source code:

<?php require_once('Connections/rfps.php'); ?>
<?php
//initialize the session
if (!isset($_SESSION)) {
  session_start();
}
?>
<html>
<head>
<script type="text/javascript">

function calc(idx) {
  var price = parseFloat(document.getElementById("cost"+idx).value)*
              parseFloat(document.getElementById("qty"+idx).value);
  //  alert(idx+":"+price);  
  document.getElementById("price"+idx).value= isNaN(price)?"0.00":price.toFixed(2);
}

function totalIt() {
  var qtys = document.getElementsByName("qty[]");
  var total=0;
  for (var i=1;i<=qtys.length;i++) {
    calc(i);  
    var price = parseFloat(document.getElementById("price"+i).value);
    total += isNaN(price)?0:price;
  }
  document.getElementById("total").value=isNaN(total)?"0.00":total.toFixed(2)                       
}      

window.onload=function() {
  document.getElementsByName("qty[]")[0].onkeyup=function() {calc(1)};
  document.getElementsByName("cost[]")[0].onkeyup=function() {calc(1)};
}
</script>
</head>

<body>
<?php
$sql = "SELECT * FROM transaction_item";
$result = mysql_query($sql,$rfps) or die($sql."<br/><br/>".mysql_error());

//start a table
echo '<form name="form1" method="post" action="">
<table  width="350px" border="1" style="border-collapse:collapse;">';
 
//start header of table
echo '<tr>
<th id="datatable" > <div align="center">No</div></th>
<th style="display:none;"><div align="center">Trans<br />Item<br />ID</div></th>
<th style="display:none;"><div align="center">Trans<br />Info<br />ID</div></th>
<th><div align="center">Style<br />No</div></th>
<th><div align="center">Item<br />Desc</div></th>
<th><div align="center">Qty</div></th>
<th><div align="center">Unit<br />Price</div></th>
<th formula="cost*qty"summary="sum"><div align="center">Total<br />Price</div></th>
</tr>';
 
//loop through all results
$i=1;
while($r=mysql_fetch_object($result)){
 
//print out table contents and add id into an array and email into an array
echo '<tr>
<td id="datatable"> <div align="center">'.$i.'</div></td>
<td style="display:none;"><div align="center">'.$r->trans_item_id.'</div></td>
<td style="display:none;"><div align="center">'.$r->trans_info_id.'</div></td>
<td><div align="center"><input type="text" id="style'.$i.'" name="style[]" value="'.$r->style_no.'" size="10" /></div></td>
<td><div align="center"><input type="text" id="item'.$i.'" name="item[]" value="'.$r->item_desc.'" size="25" /></div></td>
<td><div align="center"><input type="text" id="qty'.$i.'" name="qty[]" value="'.$r->qty.'" size="2" /></div></td>
<td><div align="center"><input type="text" id="cost'.$i.'" name="cost[]" value="'.$r->unit_price.'" size="5" /></div></td>
<td><div align="center"><input type="text" id="price'.$i.'" name="price[]" value="'.$r->total_price.'" size="6" /></div></td>
</tr>';
++$i;
}
 
//submit the form
echo'</table>
<input type="submit" name="Submit" value="Submit">
<p><strong>GRAND TOTAL: RM</strong>
<input type="text" readonly="readonly" id="total" />
<input type="button" value="Calculate Total" onclick="totalIt()" /></p>
</form>';
?>
</body>
</html>


Solution 1:[1]

Well, upon a quick glance, the problem is that you're only grabbing the first qty[] element and the first cost[] element, to add listeners to.

Have a look at the last bit of JavaScript, there -- the window.onload = function (){... part:

window.onload=function() {
  document.getElementsByName("qty[]")[0].onkeyup=function() {calc(1)};
  document.getElementsByName("cost[]")[0].onkeyup=function() {calc(1)};
}

That's great, but it means that you're only grabbing the first box of each, and sending in "qty1" and "cost1" data.

Try this, instead:

window.onload = function () {
    var qtyArr = document.getElementsByName("qty[]"),
        costArr = document.getElementsByName("cost[]"),
        length = qtyArr.length,
        i = 0, func = null;

    for (; i < length; i++) {
        func = (function (i) { return function () { calc(i + 1); }; }(i));
        qtyArr[i].onkeyup = func;
        costArr[i].onkeyup = func;
    }
};

The more-appropriate way of doing this might be to use event-delegation:

  1. Listen to onkeyup on the entire table that contains these rows.
  2. If the element that was edited (using onkeyup) is an element you want to use:

    if (event.target.name === "qty[]" || event.target.name === "cost[]") {...}

  3. Fire calc like so:

    calc( event.target.id.replace("qty","").replace("cost","") );

First way should work fine, if you want a quick-and-dirty solution.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Norguard