0

EDIT: This question is asked quite a bit. Here is the link to understanding event delegation. the direct answer is to use .on( 'event', 'selector', function); in my case that was $("fieldset").on('keyup', '.line input', multInputs);


I am building an invoice that allows users to add additional lines of service payments by clicking a button. Each line of services has a subtotal.

The problem that I am having is that I cannot get the subtotal to work for fields that are dynamically added with jQuery .before.

Here is my invoice form, with the button to add another line:

<div class="control-group">
     <label class="control-label"> Service</label><div class="controls form-inline">
      <div class="line">
       <input type="text" class="span3" id="serivceName" placeholder="Service name">
       <input type="text" class="qty span1" id="qty" placeholder="qty">
       <input type="text" class="price input-small" name="cost" id="service" placeholder="Price">
       <input type="text" class="subtotal" id="sub" name="sub" value="">
      </div>
     </div>
  </div>

 <div class="serviceHelper"></div> 
       <br />
       <br />
 <div class="pull-right span2">
      Total: <span id="income_sum" class="g_total"/>
       </div>

       <div>
      <a  id='addService' class="btn btn-success btn-small">Add another Product or Service</a><br />
                    </div>

Here is the subtotal function in jQuery:

$(".line input").keyup(multInputs);
function multInputs() {
   var mult = 0;

      //iterate through the .line div and find values
   $(".line").each(function () {    
       var $val1 = $('.qty', this).val();
       var $val2 = $('.price', this).val();
       var $total = $val1 * $val2;

         //make the subtotal input equal the total var
       $(this).find(".subtotal").val($total);

         //add the subtotals together to make the invoice total
       var $form = $('#wizard');
       var $summands = $form.find('.subtotal');
       var $sumDisplay = $('#income_sum');
       var $sum = 0;
       $summands.each(function ()
        {
            var value = Number($(this).val());
            if (!isNaN(value)) $sum += value;
        });
    $sumDisplay.text(sum);
    });

}

The invoice line that is added by the user is a copy of the control group already on the page, but modified because I am inserting it into jQuery-Steps wizard.

    $("#addService").click(function(){
    $(".serviceHelper").before("<div class='control-group'> <div class='controls form-inline'>"+
       "<div class='line'>"+
       "<input type='text' class='span3' id='serivceName' placeholder='Service name'> "+
       "<input type='text' class='qty span1' id='qty' placeholder='qty'> "+
       "<input type='text' class='price input-small' name='cost' id='service' placeholder='Price'> "+
       "<input type='text' class='subtotal' id='sub' name='sub' value=''>"+
       "</div> </div> </div>");
     });

Do you see why the subtotal will work for the inputs on the page, but not for those dynamically added? TIA

retrograde
  • 2,979
  • 6
  • 28
  • 54
  • 3
    This question was asked many-many times. For dynamically created elements use delegated event handlers with [.on()](http://api.jquery.com/on/). For example, `$('.control-group').on('keyup', ".line input", multInputs);` – Regent Oct 17 '14 at 12:09
  • in fact it gets asked many times a day! Read up on event delegation – charlietfl Oct 17 '14 at 12:16

1 Answers1

1

jQuery is only aware of the elements in the page at the time that it runs, so new elements added to the DOM are unrecognized by jQuery. To combat that you have to use event delegation, bubbling events from newly added items up to a point in the DOM that was there when jQuery ran on page load. Many people use document as the place to catch the bubbled event, but it isn't necessary to go that high up the DOM tree. As a matter of course you should delegate to the nearest parent that exists at the time of page load.

$(document).on('keyup', ".line input", multInputs); 

The keyup on .line input now bubbles up to the document level and can be handled there.

Community
  • 1
  • 1
Jay Blanchard
  • 34,243
  • 16
  • 77
  • 119
  • 1
    Thank you, that worked perfect. I bound it to "fieldset": $("fieldset").on('keyup', '.line input', multInputs); – retrograde Oct 17 '14 at 12:50