Robert Robert - 3 months ago 7
Javascript Question

Duplicating dynamic rows in table for some reason when exiting and re-entering?

Let me explain what this does, should do and what it should not do.

This is basically adding a table row to a table every time you click the add row please button.

It is only meant to append 1 row, which it does and does well, until you click the done button which makes the lightbox effect go away.

But if they go back on there again it for some reason duplicates the effect.

If you exit again and then re-enter it now triples the rows.

Can anyone please point me in the right direction to why this may be?

Here's a jsfiddle so you can see what I mean

Here is the html code:

<a href="#" id="quottable">Click here</a>
<div class="model-background">

<div class="model-wrap">

<div class="ubl-section-5">

<div class="table-responsive" id="jquerytable">

<table class="table table-bordered">
<thead>
<tr>
<th class="text-center">Item Description</th>
<th class="text-center">Cost</th>
<th class="text-center">Sell At</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total Cost</th>
<th class="text-center">Total Sell</th>
<th class="text-center">Margin</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="text" class="form-control" id="jquerytabledescription" value="" placeholder="Unit Item Name">
</td>
<td class="text-center">
<input type="text" class="form-control" id="jquerytablecosts" value="" placeholder="Enter Cost Without &pound; Symbol">
</td>
<td class="text-center">
<input type="text" class="form-control" id="jquerytablesell" value="" placeholder="Enter Selling Price Without &pound; Symbol">
</td>
<td class="text-center">
<input type="text" class="form-control" id="jquerytablequantity" value="" placeholder="Quantity">
</td>
<td class="text-center" id="jqueryTotalcost">X</td>
<td class="text-center" id="jquerySellingcost">X</td>
<td class="text-center" id="jquerymargin">X</td>
</tr>
</tbody>
</table>

<p><a href="#" class="btn btn-default btn-block" id="anotherrowplease">Need Another Row Please</a></p>

<p><a href="#" class="btn btn-danger btn-block" id="ubldone4">Done</a></p>

</div>

</div>

</div>

</div>


I am using Bootstrap for the initial css and also here is a little custom:

.model-background {
background-color: rgba(0, 0, 0, 0.8);
width: 100%;
height: 100%;
top: 0px;
left: 0px;
position: fixed;
display: none;
}

.model-wrap {
width: 80%;
height: auto;
margin: 100px auto 0px auto;
padding: 40px;
float: none;
background-color: white;
}

.ubl-section-5 {
display: none;
}


Then finally I am running jQuery 3.1 as well as this code here:

jQuery(document).ready(function($) {

"use strict";

$('#quottable').on('click', function() {

var idName = '';
var lastChar = '';

var jc = '';
var js = '';
var jq = '';

var jfc = '';
var jfs = '';
var jmgn = '';

$('.ubl-section-5').show();
$('.model-background').fadeIn(500);

$.cookie('howMany', 1);

$('#anotherrowplease').on('click', function(event) {

event.preventDefault();

var newRow = '';

var checkHowmany = parseInt($.cookie('howMany'));
if (checkHowmany === 1) {
checkHowmany = 2;
}

$.cookie('howMany', checkHowmany + 1);

newRow += '<tr>';
newRow += '<td><input type="text" class="form-control" id="jquerytabledescription' + checkHowmany + '" value="" placeholder="Unit Item Name"></td>';
newRow += '<td class="text-center"><input type="text" class="form-control" id="jquerytablecosts' + checkHowmany + '" value="" placeholder="Enter Cost Without &pound; Symbol"></td>';
newRow += '<td class="text-center"><input type="text" class="form-control" id="jquerytablesell' + checkHowmany + '" value="" placeholder="Enter Selling Price Without &pound; Symbol"></td>';
newRow += '<td class="text-center"><input type="text" class="form-control" id="jquerytablequantity' + checkHowmany + '" value="" placeholder="Quantity"></td>';
newRow += '<td class="text-center" id="jqueryTotalcost' + checkHowmany + '">X</td>';
newRow += '<td class="text-center" id="jquerySellingcost' + checkHowmany + '">X</td>';
newRow += '<td class="text-center" id="jquerymargin' + checkHowmany + '">X</td>';
newRow += '</tr>';

$('#jquerytable tbody').append(newRow);

//calculate each and every item
$('#jquerytable input').change(function() {

//find the parent id so we know
idName = $(this).attr('id');
lastChar = idName.substr(idName.length - 1);

jc = $('#jquerytablecosts' + lastChar).val();
js = $('#jquerytablesell' + lastChar).val();
jq = $('#jquerytablequantity' + lastChar).val();

if (jc == '' || jc == 'undefined' || jc == null) {
jc = 0;
}
if (js == '' || js == 'undefined' || js == null) {
js = 0;
}
if (jq == '' || jq == 'undefined' || jq == null) {
jq = 0;
}

jfc = jc * jq;
jfs = js * jq;
jmgn = 100 - ((jfc / jfs) * 100);

if ($.isNumeric(lastChar) !== false) {

//get the values of the 3 items and then add them to the final results
$('#jqueryTotalcost' + lastChar).html('&pound;' + jfc);
$('#jquerySellingcost' + lastChar).html('&pound;' + jfs);
$('#jquerymargin' + lastChar).html(jmgn.toFixed(2) + '%');

}

});

});

//calculate each and every item
$('#jquerytable input').change(function() {

//find the parent id so we know
idName = $(this).attr('id');
lastChar = idName.substr(idName.length - 1);

jc = $('#jquerytablecosts').val();
js = $('#jquerytablesell').val();
jq = $('#jquerytablequantity').val();

if (jc == '' || jc == 'undefined' || jc == null) {
jc = 0;
}
if (js == '' || js == 'undefined' || js == null) {
js = 0;
}
if (jq == '' || jq == 'undefined' || jq == null) {
jq = 0;
}

jfc = jc * jq;
jfs = js * jq;
jmgn = 100 - ((jfc / jfs) * 100);

if ($.isNumeric(lastChar) === false) {

//get the values of the 3 items and then add them to the final results
$('#jqueryTotalcost').html('&pound;' + jfc);
$('#jquerySellingcost').html('&pound;' + jfs);
$('#jquerymargin').html(jmgn.toFixed(2) + '%');

}

});

$('#ubldone4').on('click', function(ev) {

ev.preventDefault();

$('.model-background').fadeOut(500);
$('.ubl-section-5').hide();

});

});

});

Answer

This is a common mistake for those new to JS and can be difficult to understand at first. The reason this is happening is because you are rebinding the click handler for $('#anotherrowplease') each time that $('#quottable') is clicked. You can fix this a number of ways. I would move the click handler for $('#anotherrowplease') outside of the handler for $('#quottable') since that only needs to run once (not each time that $('#quottable') is clicked).

https://jsfiddle.net/DTcHh/24362/

There are times you need to attach event listeners to dynamically created content such as the inputs in the new row you are adding. However, you don't want to continue rebinding to the existing elements. To solve this, I'd say most people choose to use Event Delegation, you could also use a class to keep track of what elements already have listeners and add :not(.myClass) to the end of your selector, or simply call .off() before rebinding.

Comments