3

I want to make a list of group inputs allow user to dynamically let user add/remove group row:

<div id="div-form-denominations" class="form-denominations">
    <div id="row-0" class="form-denomination">
        <div class="form-field">
        <div class="form-field">
        <div class="form-field">
        <input id="_denominations[0].id.reference" class="removableHiddenOrder" type="hidden" name="denominations[0].id.reference" value="87-070329-034COP-4444">
        <input id="_denominations[0].denomination" class="removableHiddenDenom" type="hidden" name="denominations[0].denomination" value="10000">
        <a id="deleteBtn-[0]" class="action-link delete-denomination" href="#">
        <div class="spacer"></div>
    </div>
    <div id="row-1" class="form-denomination">
        <div class="form-field">
        <div class="form-field">
        <div class="form-field">
        <input id="_denominations[1].id.reference" class="removableHiddenOrder" type="hidden" name="denominations[1].id.reference" value="87-070329-034COP-4444">
        <input id="_denominations[1].denomination" class="removableHiddenDenom" type="hidden" name="denominations[1].denomination" value="">
        <a id="deleteBtn-[1]" class="action-link delete-denomination" href="#">
        <div class="spacer"></div>
    </div>
    <div id="row-2" class="form-denomination">
        <div class="form-field">
        <div class="form-field">
        <div class="form-field">
        <input id="_denominations[2].id.reference" class="removableHiddenOrder" type="hidden" name="denominations[2].id.reference" value="">
        <input id="_denominations[2].denomination" class="removableHiddenDenom" type="hidden" name="denominations[2].denomination" value="">
        <a id="deleteBtn-[2]" class="action-link delete-denomination" href="#">
    <div class="spacer"></div>
    </div>
    <div id="row-3" class="form-denomination">
        .......
    </div>

So each row include a group of form-field which include an input or select component(not show in above code) and some hidden fields and a delete link to remove current row from view.

Also I create a link to dynamic add a new row into the section

var rowTemplate = null;
j(document).ready(function() {
    // Save the row template
    rowTemplate = j('.form-denomination:first-child').clone();

    j("#add_link").click(function() {
            add_denomination();
    });
});

and here is the content of add_denomination function that clone the first row and replace any cloned id with new index, and append the cloned row after last row of the list.

function add_denomination() {
    var index = j('.form-denomination').length; 
    // set the new row id
    var newRowId = 'row-' + index;
    var newRow = rowTemplate.clone().attr('id', newRowId);

    // Replace the id/name attribute of each input control
    newRow.find('div, input, select, a').each(function() {
        replaceAttribute(j(this), 'id', index);
        replaceAttribute(j(this), 'name', index);
        j(this).val('');
    });

    // add new element to the DOM
    newRow.appendTo('.form-denominations');
    alert("new list size = " + j(".delete-denomination").length);
    console.log("DONE!");
}

each time click on the add-link the pop up alert show the new list size (j(".delete-denomination").length increment by 1), which in my understanding, new row appended successfully.

The problem is the following method

// delete denomination row
j('.delete-denomination').click(function () {
    j(this).parent().remove();
}

ONLY WORKS FOR THE NON-CLONED ROW !!! Using firebug I can clearly see the appended row is successfully appended with same structure and same element as original rows but only difference is the id.

However, each time when I click on deleteBtn-[i], in which i is the cloned/appended row's index, the code even not going into the j('.delete-denomination').click() function, which in my understanding, Dom or jquery didn't recognize the new rows hence the failure of identifying the link by jQuery. It's kind of contradictory to the previous alert message that telling the size of list has grown.

But when I click on deleteBtn-[i] where i is the non-cloned row, everything works fine...

So the question is: how to append/add new doms and make them identified by jQuery or Dom? What is wrong in above processing? Is there any way to refresh the list so that Dom/jQuery understand the appended rows from all perspective?

Radiotrib
  • 629
  • 5
  • 8
Dreamer
  • 7,333
  • 24
  • 99
  • 179
  • 3
    Classic event delegation issue, possible duplicate, http://stackoverflow.com/questions/6658752/jquery-click-event-doesnt-work-on-dynamically-generated-elements – Benjamin Gruenbaum Mar 22 '13 at 02:54
  • Not what you're asking about, but why are you assigning IDs? Are the dynamic elements ever actually referenced by ID? (I would think giving them a common class would be more appropriate.) – nnnnnn Mar 22 '13 at 03:03

3 Answers3

3

jQuery 1.7+

j(".form-denomination").on("click", ".delete-denomination", function(){
    j(this).parent().remove(); 
});

jQuery 1.3+

j(".delete-denomination").live("click", function(){ 
    j(this).parent().remove(); 
 });               

jQuery 1.4.3+

j(".form-denomination").delegate(".delete-denomination", "click", function(){ 
   j(this).parent().remove(); 
}); 
jk.
  • 14,365
  • 4
  • 43
  • 58
  • Thank you and it's kind of embarrassing to ask: is there a solution to this issue for jquery 1.4? We are running on very old legacy library by requirement.... – Dreamer Mar 22 '13 at 02:58
  • 1
    ha.. follow @Benjamin Gruenbaum's link I use `.live` and it works! Thanks everyone ! – Dreamer Mar 22 '13 at 03:01
1

The problem is a matter of order and when expressions are evaluated. When you call jQuery with a selector, the selector is evaluated at that time to select the matching elements which exist at that time. The click handler is then registered to only those elements. Elements which are created later are, naturally, not affected.

One solution, demonstrated in another example, uses jQuery's "live events" to apply the selector at the time each event is fired to determine what elements, if any, it would match. There is a performance implication to this approach.

Another solution is to register the desired event handler on the newly created elements when you create them.

dsh
  • 12,037
  • 3
  • 33
  • 51
1

Add 'true' to the clone method in order to copy the data as well as the events attached to the original element.

rowTemplate = j('.form-denomination:first-child').clone(true);

This is disabled by default. Here is the clone documentation: https://api.jquery.com/clone/

P.s. You don't need the click function within the document.ready and it won't bind until after the click.

Jordan Casey
  • 955
  • 9
  • 16