Search code examples
javascriptjqueryloopsjquery-selectors

Rewriting JavaScript code with consequent numbers in the names of ids


I'm trying to apply a function to input field with ids that contain consequent numbers (ie. price1, price2, price3), etc.

There's no problem with the first row of field that are defined for a start. But further input fields are dynamically added by a jQuery function and their number is not known in advance.

I hoped it would be an easy loop to apply:

var i=1;
$("#quantity"+i).keyup(function() {
    var price= $("#price"+i).val();
    var quantity= $(this).val();
    var value= price*quantity;
    var value=value.toFixed(2); /* rounding the value to two digits after period */
    value=value.toString().replace(/\./g, ',') /* converting periods to commas */
    $("#value"+i).val(value);
});

So far so good - the outcome of the multiplication properly displays in the id="value1" field after the "quantity" field is filled up.

Now further fields should follow the pattern and calculate the value when the quantity is entered - like this:

[price2] * [quantity2] = [value2]
[price3] * [quantity3] = [value3]

etc.

So the code follows:

$('#add_field').click(function(){ /* do the math after another row of fields is added */
var allfields=$('[id^="quantity"]'); 
var limit=(allfields.length); /* count all fields where id starts with "quantity" - for the loop */
for (var count = 2; count < limit; count++) { /* starting value is now 2 */
$("#quantity"+count).keyup(function() {
    var cena = $("#price"+count).val();
    var quantity= $("#quantity"+count).val();
    var value= price*quantity;
    var value=value.toFixed(2);
    value=value.toString().replace(/\./g, ',')
    $("#value"+count).val(value);
});
}
});

The problem is that all further "value" fields are only calculated when "quantity2" is (re)entered and the "value2" is not calculated at all.

I guess there's a mistake while addressing fields and/or triggering the calculation.

How should I correct the code?

Just in case the "add_field" function is needed to solve the problem:

     $(document).ready(function(){  
      var i=1;  
      $('#add_field').click(function(){  
           i++;  
           $('#offer').append('<tr id="row'+i+'">
    <td><input type="text" name="prod_num[]" id="prod_num'+i+'" placeholder="Product number (6 digits)"></td><td><input type="text" name="prod_name[]" disabled></td>
    <td><input type="text" name="cena[]" id="price'+i+'" placeholder="Enter your price"></td>
    <td><input type="text" name="quantity[]" id="quantity'+i+'" placeholder="Enter quantity"></td>
    <td><input type="text" name="value[]" id="value'+i+'" disabled></td>
    <td><button type="button" name="remove_field" id="'+i+'" class="button_remove">X</button></td></tr>');
      });

Solution

  • Incrementing IDs is a lot more trouble than it is worth, especially when you start removing rows as well as adding them.

    This can all be done using common classes and traversing within the specific row instance.

    To account for future rows use event delegation.

    Simplified example:

    // store a row copy on page load
    const $storedRow = $('#myTable tr').first().clone()
    
    // delegate event listener to permanent ancestor
    $('#myTable').on('input', '.qty, .price', function(){
        const $row = $(this).closest('tr'),
              price = $row.find('.price').val(),
              qty =  $row.find('.qty').val();
        $row.find('.total').val(price*qty)
    });
    
    $('button').click(function(){
      // insert a copy of the stored row
      // delegated events will work seamlessly on new rows also
      const $newRow = $storedRow.clone();
      const prodName = 'Product XYZ';// get real value from user input
      $newRow.find('.prod-name').text(prodName)// 
      $('#myTable').append($newRow)
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <button>Add row</button>
    
    <table id="myTable">
      <tr>
        <td class="prod-name">Product 1</td>
        <td>Qty:<input type="number" class="qty" value="0"></td>
        <td>Price:<input type="number" class="price" value="0"></td>
        <td>Total:<input type="text" class="total" value="0" readonly></td>
      </tr>
      <tr>
         <td class="prod-name">Product 2</td>
        <td>Qty:<input type="number" class="qty" value="0"></td>
        <td>Price:<input type="number" class="price" value="0"></td>
        <td>Total:<input type="text" class="total" value="0" readonly></td>
      </tr>
      
    </table>

    Understanding Event Delegation