0

I've had success using .clone(true) to clone a row of inputs, including their event listeners.

However, I am also duplicating the IDs. Surprisingly though, the event listeners know which button I'm clicking, even though the IDs are duplicated.

How does .clone(true) attach event listeners to the correct elements when IDs are duplicated?

$(document).ready(function() {

  //remove event listener
  $("#remove").click(function() {
    console.log($(this).parent().parent().attr("id"));
    panelCount = $('div[id^="pos"]').length;
    console.log(panelCount);

    panelID = $(this).parent().parent().attr("id");
    if (panelCount <= 1) {
      console.log("there's only one panel yo; I won't delete it dog, I'll just clear the fields, holmes")
      $("#" + panelID).find("input:not(input[type=button])").val('')
    } else {
      $("#" + panelID).remove();

    }


  });

  /*
    MUCH LOVE TO ROKO C. BULIJAN FOR THIS
    
    HERE'S THE SOURCE:  https://stackoverflow.com/a/10127093/3772763
        
  
  
  */


  //add event listener
  $("#add").click(function() {
    // get the last DIV which ID starts with ^= "pos"
    var $div = $('div[id^="pos"]:last');

    // Read the Number from that DIV's ID (i.e: 3 from "pos3")
    // And increment that number by 1
    var num = parseInt($div.prop("id").match(/\d+/g), 10) + 1;

    // Clone it and assign the new ID (i.e: from num 4 to ID "pos4")
    //var $posClone = $div.clone().prop('id', 'pos' + num);
    //true does a deep clone to include listeners
    //think of .end() as an "edit, undo"
    var $posClone = $div.clone(true).find("input:not(input[type=button])").val('').end().prop('id', 'pos' + num);

    // Finally insert $pos wherever you want
    $div.after($posClone);

  });

  //save event listener
  $("#save").click(function() {
    var ary = [];
    var obj = {};
    $('div[id^="pos"]').each(function() {

      ary.push({
        "posCode": $(this).find("input[id=posCode]").val(),
        "posTitle": $(this).find("input[id=posTitle]").val(),
        "posScale": $(this).find("input[id=posScale]").val(),
        "posDesc": $(this).find("input[id=posDesc]").val(),
        "posQual": $(this).find("input[id=posQual]").val(),

      });

    }); //end each loop

    console.dir(ary);

  }); //end save click function

}); //end document ready click function
/* Styles go here */

#addPanelWrapper {
  height: 400px;
  width: auto;
  display: flex;
  flex-direction: column;
  background-color: powderblue;
  font-family: Arial, Helvetica, sans-serif;
}

.addPanel {
  display: block;
  height: auto;
  width: auto;
  background-color: aqua;
  padding: 5px;
}

.row {
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: auto;
  width: auto;
}

input {
  margin: 2.5px;
}

.inputError {
  border: 1px solid #c51244;
  background: #fff0f4;
}

.cell-1 {
  flex: 1;
}

.cell-2 {
  flex: 2;
}

.cell-3 {
  flex: 3;
}

.cell-4 {
  flex: 4;
}

.cell-5 {
  flex: 5;
}

.cell-6 {
  flex: 6;
}

.cell-7 {
  flex: 7;
}

.cell-8 {
  flex: 8;
}

.cell-9 {
  flex: 9;
}

.cell-10 {
  flex: 10;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="addPanelWrapper">
  <div id="pos1" class="addPanel">
    <div class="row">
      <input id="posCode" name="posCode" class="cell-1" type="text" placeholder="Position Code" required="" />
      <input id="posTitle" class="cell-3" type="text" placeholder="Position Title" />
      <input id="posScale" class="cell-1" type="text" placeholder="Salary Scale" />
    </div>
    <div class="row">
      <input id="posDesc" class="cell-8" type="textarea" placeholder="Description" />
      <input id="posQual" class="cell-8" type="textarea" placeholder="Qualifications" />
      <input id="remove" class="cell-1" type="button" value="Remove" />
    </div>
  </div>
</div>
<input id="add" value="Add" type="button" />
<input id="save" value="Save" type="button" />
NamedArray
  • 773
  • 3
  • 10
  • 25
  • 2
    It's because it copies the reference of the element which already has the event bound. The issue with duplicated `id` attributes is primarily when selecting the elements - the browsers only ever return the first one hence the others are ignored. – Rory McCrossan Dec 14 '17 at 16:05
  • 2
    An id is just a reference used to find an element. The event bindings exist on an element, or in jQuery associated with that element. Not to an id or a class or whatever selector you may have used to find the element before putting the event binding on it. When you do a clone it just takes the events associated with that element and puts them on it's clone. There is no need to consider a selector or anything for that operation. – Taplar Dec 14 '17 at 16:05
  • 2
    It's just like why when you look up an element by class, put a binding on it, and later remove the class, the event binding is still on it. Because the event is on the element after that point, regardless of any change you make to the element, with the exception of removing the event, obviously. – Taplar Dec 14 '17 at 16:07
  • So, the event listener is associated with the element, regardless of its id/class, etc.? I can inspect the element, and see an event listener per button, but there's only event handler in the code itself, which I assume must be the master copy? – NamedArray Dec 14 '17 at 16:15
  • 1
    Yes, unless you are doing a delegate binding, the only purpose of a selector serves in binding an event handler to an element is to **find** the element, nothing more. In the case of a delegate binding, the selector in the delegate is used to filter on the children of the parent that the event handler it attached to, which is evaluated for every event. So that one does matter, regarding the current state of the child when the event is generated. – Taplar Dec 14 '17 at 16:23
  • 1
    As far as if the event handler methods are unique or not, I would assume the clone would reuse the method reference and not duplicate the method in memory. However, if you are say attaching an event handler to elements in a loop and you are using anonymous methods, that can easily pollute your javascript memory with duplicate method definitions, provided you are doing the loop yourself and not doing something like `$(findLotsOfStuff).on('click', function(){});` which I would assume jQuery would use that same reference on all the elements. – Taplar Dec 14 '17 at 16:25

0 Answers0