Solace Solace - 7 months ago 70
HTML Question

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>




Answer

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.