Solace
Solace

Reputation: 9010

JQuery `length` property not working properly. Why?

JSFiddle here.

In the following SSCCE, in the click listener for button#remove, there is a alert("numberOfRows:" + numberOfRows); //check***************** statement. The statement before it calculates numberOfRows. This number always appears to be 0. The question is about it.

Please run this code. Click the Add TextField button once or more than once. Each click will add the Orange bordered table. THEN click the Remove TextField button, and you will get the alert. The problem is that this will always show the numberOfRows to be 0, no matter how many rows are actually there.

The question is why? How do I fix this?

$(document).on("click", "button.remove", function() {
  event.preventDefault();

  var currentRow = $(this).parent().parent().parent().parent().parent().parent();
  currentRow.remove();


  //update numberOfRows variable
  var parentTable = $(this).parent().parent().parent().parent().parent().parent().parent().parent();

  var numberOfRows = parentTable.children('tbody').children('tr').length;

  /*****************************************/
  /*****************************************/
  /*****************************************/
  alert("numberOfRows:" + numberOfRows); //check*****************
  /*****************************************/
  /*****************************************/
  /*****************************************/


  if (!(numberOfRows > 1)) {
    $(".remove").hide();
  }
});










$(document).on("click", "button.add", function() {
  event.preventDefault();

  var parentTable = $(this).parent().parent().parent().parent().parent().parent().parent().parent();
  var lastTableRow = parentTable.children('tbody').children('tr:last');

  //Adding the new row
  parentTable.children('tbody').append(lastTableRow.clone());

  //Reset lastRow variable 
  lastTableRow = parentTable.children('tbody').children('tr:last');
  //Reset the fields
  lastTableRow.find('table tbody tr td input').each(function() {
    $(this).val('');
  });


  //update numberOfRows variable
  var numberOfRows = parentTable.children('tbody').children('tr').length;
  alert("numberOfRows:" + numberOfRows); //check


  if (!(numberOfRows > 1)) {
    $(".remove").hide();
  } else {
    $(".remove").show();
  }

});
#outer-table {
  padding: 20px;
  border: 3px solid pink;
}
#inner-table {
  border: 3px solid orange;
}
.remove {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<table id="outer-table">
  <tbody>
    <tr>
      <td>


        <table id="inner-table">
          <tbody>
            <tr>
              <td>
                <p style="display:inline-block">Enter first complain:</p>
                <input type="text" />
              </td>
            </tr>
          </tbody>

          <tfoot>
            <tr>
              <td>
                <button class="add" id="add">Add Textfield</button>
                <button class="remove" id="remove">Remove Textfield</button>
              </td>
            </tr>
          </tfoot>
        </table>



      </td>
    </tr>
  </tbody>



  <tfoot>
    <tr>
      <td>Table Footer</td>
    </tr>
  </tfoot>

</table>

Upvotes: 1

Views: 1058

Answers (2)

rockerest
rockerest

Reputation: 10508

First, use .closest( "table" ) to get the parent table.
And .closest( "tr" ) for the row.

If you must really have nested tables, then you can still use .closest( "table" ).parents( "table" ); but this should be a serious red flag.

Finally, the reason it's not working is because you are removing the context of the remove click handler (the button) from the DOM with:

currentRow.remove();

And then immediately trying to navigate up the DOM tree with:

var parentTable = $(this).parent()[... snip ... ].parent();

However, $( this ).parent()... no longer exists, because you've deleted it.

In this scenario, .length should be 0, because your DOM navigation returns nothing.

If you fetch the parent table first, and then delete the row, it should work.

Upvotes: 2

adeneo
adeneo

Reputation: 318182

Something like this is what it should look like

$('.outer-table').on("click", ".add", function(event) {
    event.preventDefault();

    var row = $(this).closest('.inner-table').closest('tr'),
        rows = row.closest('tbody').children('tr').length + 1;

    row.after(row.clone());

    alert("numberOfRows:" + rows);

    $(".remove").toggle(rows > 1);
});

$('.outer-table').on("click", ".remove", function(event) {
    event.preventDefault();

    var row = $(this).closest('.inner-table').closest('tr'),
        rows = row.closest('tbody').children('tr').length - 1;

    row.remove();

    alert("numberOfRows:" + rows);

    if (rows < 2) $(".remove").hide();
});
.outer-table {
    padding: 20px;
    border: 3px solid pink;
}

.inner-table {
    border: 3px solid orange;
}

.remove {
    display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="outer-table">
    <tbody>
        <tr>
            <td>
                <table class="inner-table">
                    <tbody>
                        <tr>
                            <td>
                                <p style="display:inline-block">Enter first complaint :</p>
                                <input type="text" />
                            </td>
                        </tr>
                    </tbody>
                    <tfoot>
                        <tr>
                            <td>
                                <button class="add">Add Textfield</button>
                                <button class="remove">Remove Textfield</button>
                            </td>
                        </tr>
                    </tfoot>
                </table>
            </td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td>Table Footer</td>
        </tr>
    </tfoot>
</table>

Upvotes: 1

Related Questions