views:

232

answers:

2

I have a table made up of a row of 3 input elements: Price, Quanity, and Total. Under that, I have two links I can click to dynamically generate another row in the table. All that is working well, but actually calculating a value for the total element is giving me trouble. I know how to calculate the value of the first total element but I'm having trouble extending this functionality when I add a new row to the table. Here's the html:

<table id="table" border="0">
    <thead>
        <th>Price</th><th>Quantity</th><th>Total</th>
    </thead>
<tbody>
<tr>
    <td><input id ="price" type = "text"></input></td>
    <td><input id ="quantity" type = "text"></input></td>
    <td><input id ="total" type = "text"></input></td>
</tr>
</tbody>
</table>
<a href="#" id="add">Add New</a>
<a href="#" id="remove">Remove</a>

Here's the jquery I'm using:

$(function(){
  $('a#add').click(function(){
    $('#table > tbody').append('<tr><td><input id ="price" type = "text"></input></td><td><input id ="quantity" type = "text"></input></td><td><input id ="total" type = "text"></input></td></tr>');  
   });
  $('a#remove').click(function(){
  $('#table > tbody > tr:last').remove();
  });
});

$(function(){
    $('a#calc').click(function(){
    var q = $('input#quantity').val();
    var p = $('input#price').val();
    var tot = (q*p);
    $('input#total').val(tot);
    });
 });

I'm new to jquery so there's probably a simple method I don't know about that selects the relevant fields I want to calculate. Any guidance would be greatly appreciated.

+1  A: 

You're creating duplicate 'id' values for those new table rows. An id is supposedly to be unique within any one page (it can be used on multiple pages, but only once per page).

The $('input#quantity') will only return the first (or maybe last) instance of the id it finds, since it's not supposed to occur multiple times.

A workaround would be to change all the ids to name=, then do something like this (in pseudo-code:

var rows = $('table').getElementsByTagName('tr');
for (var i = 0; i < rows.length; i++) {
    var q = ... extract quantity field from this row;
    var p = ... extract price field from this row;
    ... do calculation as usual
    ... store result in the total field in this row
}
Marc B
+2  A: 

You cannot have multiple elements that share the same ID. An ID has to be unique throughout the whole document.

That said, you have to change your code to use classes:

<table id="table" border="0">
    <thead>
        <th>Price</th><th>Quantity</th><th>Total</th>
    </thead>
<tbody>
<tr>
    <td><input class="price" type = "text"></input></td>
    <td><input class="quantity" type = "text"></input></td>
    <td><input class="total" type = "text"></input></td>
</tr>
</tbody>
</table>
<a href="#" id="add">Add New</a>
<a href="#" id="remove">Remove</a>

And:

$(function(){
  $('a#add').click(function(){
    $('#table > tbody').append('<tr><td><input class ="price" type = "text"></input></td><td><input class ="quantity" type = "text"></input></td><td><input class ="total" type = "text"></input></td></tr>');  
   });
  $('a#remove').click(function(){
  $('#table > tbody > tr:last').remove();
  });
});

In order to provide a complete solution, where is your link with ID calc. How should the calculation work? Should every row has its own calculation button or do you have one global button the calculates the values for every row?

If the latter you can use something like this:

$('a#calc').click(function(){
    $('#table tr').each(function() {
        $(this).find('.total').val(
            parseFloat($(this).find('.quantity').val())*
            parseFloat($(this).find('.price').val())
        );
    }
}

Explanation:

When you click the link, the function loops over every row in the table, searches for the to input fields with class quantity and price and puts the result into the input with class total in that row.

Note that I use parseFloat to make sure to use numbers for the calculation.


Also note that you don't have to use $(function(){}) around every single piece of JS code you create. Put everything that needs to be executed after the DOM loaded (because you access elements from your code) into this function. So you should do:

$(function() {
    $('a#add').click(function(){
         // .....
    }:

    $('a#calc').click(function(){
        // ...
    };
}

Update: If you want to use the .blur() method, here is an example:

$('#table').delegate('input.total' , 'blur', function(){
    var row = $(this).closest('tr');
    $(this).val(
            $('.quantity', row).val())*
            $('.price', row).val())
    );
});

.closest() gets the closest parent that matches the selector (in this case the row) and the rest is similar to the first function I wrote (getting the values of the particular row.

I use the newer method delegate() here, but you can also use .live(). Note that you have to use one of them because your are creating the row dynamically and at the time e.g. $(input.total).blur(function(){}) would be run (when the DOM is loaded) no row is created yet.

Felix Kling
Sorry I meant to remove that a#calc from my code and maybe use jquery's blur and focus events so that the .total box would automatically be calculated. You're helping me better understand this though. Thanks.
I tried using your code, but the your iterator function doesn't seem to work (I click on the calc link and nothing happens)
@user306472: Mmh I should work, I will have a look again... note the update on my answer about using e.g. `blur`.
Felix Kling
@user306472: Have you changed your HTML from using IDs to using classes`? Otherwise it won't work. Also for debugging you should user Firefox with Firebug to check for any JS errors or just insert an `alert()` before `$(this).find('.total')...` to see whether the function is called.
Felix Kling
Thanks for your help! I had misnamed something. Your code was fine.