1

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>');
      });
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Dobromir
  • 25
  • 3
  • 1
    Welcome to Stack Overflow. Please provide a Minimal, Reproducible Example: https://stackoverflow.com/help/minimal-reproducible-example You may also want to take the Tour: https://stackoverflow.com/tour/ – Twisty Apr 25 '21 at 19:57
  • 1
    `id`s used with dynamically created elements are hard to use and also a bit error prone. You could take the advantage of [event delegation](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) and the structure of the table instead. – Teemu Apr 25 '21 at 19:59
  • ...you can even use classes instead: apply the same class for the elements you'd like to give numbered IDs, and you can either loop through the elements with the class or access the *nth* element having the class. – FZs Apr 25 '21 at 20:09

2 Answers2

2

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

charlietfl
  • 170,828
  • 13
  • 121
  • 150
0

The first thing to consider is that you can get the length of a selector. So for example:

var count = $("input").length; 

If there is one, value here would be 1. if there are four, the value would be 4.

You can also use .each() option to itereate each of the items in the selector.

$('#add_field').click(function(){
  var allFields = $('[id^="quantity"]'); 
  allFields.each(function(i, el){
    var c = i + 1;
    $(el).keyup(function() {
      var price = parseFloat($("#price" + c).val());
      var quantity = parseInt($(el).val());
      var value = price * quantity;
      value = value.toFixed(2);
      value = value.toString().replace(/\./g, ',');
      $("#value" + c).val(value);
    });
  });
});

You could also create relationship based on the ID itself.

$(function() {
  function calcTotal(price, qnty) {
    return (parseFloat(price) * parseInt(qnty)).toFixed(2);
  }

  $('#add_field').click(function() {
    var rowClone = $("#row-1").clone(true);
    var c = $("tbody tr[id^='row']").length + 1;
    rowClone.attr("id", "row-" + c);
    $("input:eq(0)", rowClone).val("").attr("id", "prod_num-" + c);
    $("input:eq(1)", rowClone).val("").attr("id", "price-" + c);
    $("input:eq(2)", rowClone).val("").attr("id", "quantity-" + c);
    $("input:eq(3)", rowClone).val("").attr("id", "value-" + c);
    $("button", rowClone).attr("id", "remove-" + c);
    rowClone.appendTo("table tbody");
  });

  $("table tbody").on("keyup", "[id^='quantity']", function(e) {
    var $self = $(this);
    var id = $self.attr("id").substr(-1);
    if ($("#price-" + id).val() != "" && $self.val() != "") {
      $("#value-" + id).val(calcTotal($("#price-" + id).val(), $self.val()));
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="add_field">Add Field</button>
<br />
<h2>Product</h2>
<table>
  <thead>
    <tr>
      <td>Number</td>
      <td>Name</td>
      <td>Price</td>
      <td>Quantity</td>
      <td>Total</td>
      <td></td>
  </thead>
  <tbody>
    <tr id="row-1">
      <td><input type="text" name="prod_num[]" id="prod_num-1" placeholder="Product number (6 digits)"></td>
      <td><input type="text" name="prod_name[]" disabled></td>
      <td><input type="text" name="cena[]" id="price-1" placeholder="Enter your price"></td>
      <td><input type="text" name="quantity[]" id="quantity-1" placeholder="Enter quantity"></td>
      <td><input type="text" name="value[]" id="value-1" disabled></td>
      <td><button type="button" name="remove_field" id="remove-1" class="button_remove">X</button></td>
    </tr>
  </tbody>
</table>
Twisty
  • 30,304
  • 2
  • 26
  • 45
  • Thanks for your effort but there's something wrong with this code and in terms of the final result it's not far away from what I achieved. If you add further rows the calculations are wrong... – Dobromir Apr 27 '21 at 06:17