1

I have a table in which there are 5 rows already, I have added js code so that when a user types something in the last row, it gets cloned (works fine) but when a user types something in the cloned row, it's not getting cloned. Can someone, please help me as to why it's not working?

$(document).ready(function() {
  $(".keynum").change(function(event) {
    $("#frmOK").val("1");
    var pid = $('#p').val();

    lastval = $(".keynum").last().val();
    if (lastval != "") {
      var $tableBody = $('#tblKeyNumQty').find("tbody"),
        $trLast = $tableBody.find("tr:last"),
        $trNew = $trLast.clone();
      $trLast.after($trNew);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table id="tblKeyNumQty" class="table table-responsive table-striped table-bordered">
  <tbody>
    <tr>
      <th align="center">Key Number</th>
      <th align="center">Quantity</th>
    </tr>
    <tr>
      <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum" value="" onkeypress="return event.keyCode != 13;" /></td>
      <td><input name="qty[]" type="number" min="2" class="form-control" value="2" onkeypress="return event.keyCode != 13;" /></td>
    </tr>
    <tr>
      <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum" value="" onkeypress="return event.keyCode != 13;" /></td>
      <td><input name="qty[]" type="number" min="2" class="form-control" value="2" onkeypress="return event.keyCode != 13;" /></td>
    </tr>
    <tr>
      <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum" value="" onkeypress="return event.keyCode != 13;" /></td>
      <td><input name="qty[]" type="number" min="2" class="form-control" value="2" onkeypress="return event.keyCode != 13;" /></td>
    </tr>
    <tr>
      <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum" value="" onkeypress="return event.keyCode != 13;" /></td>
      <td><input name="qty[]" type="number" min="2" class="form-control" value="2" onkeypress="return event.keyCode != 13;" /></td>
    </tr>
    <tr>
      <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum" value="" onkeypress="return event.keyCode != 13;" /></td>
      <td><input name="qty[]" type="number" min="2" class="form-control" value="2" onkeypress="return event.keyCode != 13;" /></td>
    </tr>
  </tbody>
</table>
ankitkanojia
  • 3,072
  • 4
  • 22
  • 35
Noob Coder
  • 68
  • 6
  • 1
    Does this answer your question? [Event binding on dynamically created elements?](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) – Andreas Jan 23 '20 at 10:47

2 Answers2

2

When dealing with dynamically added DOM elements, you must delegate events to an ancestor element that has existed since the webpage was loaded. In this situation, the <form> (not provided in HTML of question -- but evident that one exists by the jQuery provided). Even though the <table> (even document for that matter -- although not recommended) is also an ancestor element of all <input>s, the "change" event only applies to <form> elements and form controls (ex. <input>, <select>, etc.).

99% of the time the .on() method is the best way to delegate events. The following is the most effective pattern:

$("selectorOfAncestor").on("eventType", "selectorOfTargets", eventHandler);
// So if you had <form id="OK"> wrapped around the <table>...
$("#OK").on("change", ".keynum:last", cloneLast)
// When defining the event handler (ex. function cloneLast(e) {... ) ".keynum:last" = this

jQuery allows you to conveniently "blanket over" many elements in a very flexible and accurate way -- so you should never use on-event attributes like this abomination:

<input ... onkeypress="return event.keyCode != 13;">

Use one line of code -- not 100 lines of the same code:

 $('input').on('keypress', function(e) {
    return e.keyCode != 13
 });

Also, note that I corrected the <table> a little by wrapping a <thead> around the <th> and applied the Bootstrap class .text-center to the <thead>. The attribute [align] is deprecated and if you don't have Bootstrap, use the CSS property text-align instead.

One more piece of advice: If you place your jQuery/JavaScript before the closing body tag (ie </body>), then you don't need to wrap everything in $(document).ready({...}). Regardless of whether you do or not, placing <script> before the closing </body> tag is 99% the most efficient place to have it.

Demo

$('#OK').on('change', '.keynum:last', cloneLast);

function cloneLast(e) {
  if ($(this).val() !== '') {
    $(this).closest('tr').clone().appendTo($(this).closest('tbody'));
  }
}

$('input').on('keypress', function(e) {
  return e.keyCode != 13
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">


<form id='OK'>
  <table id="tblKeyNumQty" class="table table-responsive table-striped table-bordered">
    <thead class='text-center'>
      <tr>
        <th>Key Number</th>
        <th>Quantity</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum"></td>
        <td><input name="qty[]" type="number" min="2" class="form-control" value="2"></td>
      </tr>
      <tr>
        <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum"></td>
        <td><input name="qty[]" type="number" min="2" class="form-control" value="2"></td>
      </tr>
      <tr>
        <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum"></td>
        <td><input name="qty[]" type="number" min="2" class="form-control" value="2"></td>
      </tr>
      <tr>
        <td><input name="keynum[]" placeholder="SKU-123" class="form-control keynum"></td>
        <td><input name="qty[]" type="number" min="2" class="form-control" value="2"></td>
      </tr>
    </tbody>
  </table>
</form>

<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • There's a perfect dupe target for this. Why do you add just another copy of that as answer instead of voting to close? – Andreas Jan 23 '20 at 12:39
  • Mostly because of the on-event attribute irks me and a comment never seems to do it justice. Also the importance of selecting the proper ancestor for the change event. – zer00ne Jan 23 '20 at 13:49
  • Hi, Thanks for the reply. Its working but my ajax code to check the keynumber is still not working for the cloned rows, my ajax code is following: – Noob Coder Feb 03 '20 at 10:21
  • Hi @TapanBhanot, transfer that ajax code to a new question and delete the answer on this post. If this answer resolved the question of ***this post***, then accept it by clicking the check ✅ of this answer. I'll be happy to help you resolve your problem on a new question just notify me when it's ready by leaving a comment on this answer (or this question if you prefer). – zer00ne Feb 03 '20 at 23:10
1

Add the change handler to newly created table rows as well:

function keynumChange(event) {
        $("#frmOK").val("1");
        var pid = $('#p').val();

        lastval = $(".keynum").last().val();
        if (lastval != "") { 
            var $tableBody = $('#tblKeyNumQty').find("tbody"),
            $trLast = $tableBody.find("tr:last"),
            $trNew = $trLast.clone();
            $trNew.change(keynumChange); // <- mind this!
            $trLast.after($trNew);
        }

}
$( document ).ready(function() {
    $( ".keynum" ).change(keynumChange);
});

Since they were added after the $(document).ready, this will not apply to those newly added rows by default.

Damocles
  • 1,287
  • 1
  • 7
  • 9