1

I have developed a code to build out salary costs for a project. The problem is that only the first row is calculating.

I have searched and found a few forums discussing the same problem but every approach/code looks completely different. Also, I have copied whole coding examples from youtube videos/forums to replicate a solution and none seems to work. I know there may be issues with ID/class but being new to coding, everything just confuses me. Help!

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
    <meta charset="utf-8">
    <title></title>
</head>

<body>
    <form name='vetCosting'>
        <h3> Salaries </h3>
        <table ID="salaries">
            <tr>
                <th>Classification</th>
                <th>Hourly rate</th>
                <th>Hours</th>
                <th>Cost</th>
                <th>Comments</th>
                <th>Type</th>
                <th></th>
                <th></th>
            </tr>
            <tr>
                <td>
                    <select>
                        <option value="T1.0">Teacher 1.0</option>
                        <option value="T1.1">Teacher 1.1</option>
                        <option value="T1.2">Teacher 1.2</option>
                        <option value="T1.3">Teacher 1.3</option>
                    </select>
                </td>
                <td><input type="number" name="hourlyRate" value="" onFocus="startCalc();" onBlur="stopCalc()"></td>
                <td><input type="number" name="salaryHours" value="" onFocus="startCalc();" onBlur="stopCalc()"></td>
                <td><input type="number" name="salaryCost" readonly="readonly"></td>
                <td><input type="text" name="salComments"></td>
                <td><input type="text" name="salType"></td>
                <td><input type="button" value="+" ; onclick="ob_adRows.addRow(this)"></td>
                <td><input type="button" value="-" ; onclick="ob_adRows.delRow(this)"></td>
            </tr>
        </table>
    </form>

    <script>
        function startCalc() {
            interval = setInterval("calc()", 2);
        }

        function calc() {
            hrRate = document.vetCosting.hourlyRate.value;
            salHours = document.vetCosting.salaryHours.value;
            document.vetCosting.salaryCost.value = ((hrRate * 1) * (salHours * 1));
        }

        function stopCalc() {
            clearInterval(interval);
        }
    </script>

    <script>
        function adRowsTable(id) {
            var table = document.getElementById(id);
            var me = this;
            if (document.getElementById(id)) {
                var row1 = table.rows[1].outerHTML;

                function setIds() {
                    var tbl_id = document.querySelectorAll('#' + id + ' .tbl_id');
                    for (var i = 0; i < tbl_id.length; i++) tbl_id[i].innerHTML = i + 1;
                }

                me.addRow = function (btn) {
                    btn ? btn.parentNode.parentNode.insertAdjacentHTML('afterend', row1) :
                        table.insertAdjacentHTML('beforeend', row1);
                    setIds();
                }

                me.delRow = function (btn) {
                    btn.parentNode.parentNode.outerHTML = '';
                    setIds();
                }
            }
        }
        var ob_adRows = new adRowsTable('salaries');
    </script>
</body>

</html>

I would like to be able to add and remove rows with calculations computing correctly for every row based on data inputs.

Anshu
  • 1,277
  • 2
  • 13
  • 28
Pan
  • 53
  • 6

1 Answers1

0

My first step would be to change your code from using setInterval() because that is running every 2 milliseconds even when there is no change in the input and the user is simply sitting there. I'd change it to a onKeyUp event that fires much less frequently.

That's done by simply changing your inputs to this:

<td><input type="number" name="hourlyRate" value="" onKeyUp="calc();"></td>

So we get rid of the startCalc() and stopCalc() functions.

Now, once we have multiple rows, we need a way to identify each row. So we give your first row an id of 'row_0' and also pass it through your calc() functions as follows:

<td><input type="number" name="hourlyRate" value="" onKeyUp="calc('row_0');"></td>

We then update your calc() method to this, so that it can use each row individually:

function calc(id) {
    var row = document.getElementById(id);
    var hrRate = row.querySelector('input[name=hourlyRate]').value;
    var salHours = row.querySelector('input[name=salaryHours]').value;
    row.querySelector('input[name=salaryCost]').value = ((hrRate * 1) * (salHours * 1));
}

Next, upon clicking the buttons, this error is fired:

Uncaught ReferenceError: ob_adRows is not defined at HTMLInputElement.onclick

To change this, we'll change the function that you've written. Let's first prepare a template for each row, and then simply append it to the innerHTML of the table. However, this won't work because it will also refresh the entire table, hence wiping out data from our existing rows too. So we use this to make a new HTML node with of a row with the id 'row_x':

function newRowTemplate(rowCount) {
    var temp = document.createElement('table');
    temp.innerHTML = `<tr id='row_${rowCount}'>
        <td>
            <select>
                <option value="T1.0">Teacher 1.0</option>
                <option value="T1.1">Teacher 1.1</option>
                <option value="T1.2">Teacher 1.2</option>
                <option value="T1.3">Teacher 1.3</option>
            </select>
        </td>
        <td><input type="number" name="hourlyRate" value="" onKeyUp="calc('row_${rowCount}');"></td>
        <td><input type="number" name="salaryHours" value="" onkeyUp = "calc('row_${rowCount}');"></td>
        <td><input type="number" name="salaryCost" readonly="readonly"></td>
        <td><input type="text" name="salComments"></td>
        <td><input type="text" name="salType"></td>
        <td><input type="button" value="+" ; onclick="addRow()"></td>
        <td><input type="button" value="-" ; onclick="removeRow(this)"></td>
    </tr>`;

    return temp.firstChild;
}

We directly make our new functions:

function addRow() {
    var newRow = newRowTemplate(rowCount);
    table.appendChild(newRow);
    rowCount += 1;
}

function removeRow(el) {
    el.parentNode.parentNode.remove();
    rowCount -= 1;
}

And finally, we use these new functions in our original elements as follows:

<td><input type="button" value="+" ; onclick="addRow()"></td>
<td><input type="button" value="-" ; onclick="removeRow(this)"></td>

Here's the final result:

function calc(id) {
  var row = document.getElementById(id);
  var hrRate = row.querySelector('input[name=hourlyRate]').value;
  var salHours = row.querySelector('input[name=salaryHours]').value;
  row.querySelector('input[name=salaryCost]').value = ((hrRate * 1) * (salHours * 1));
}

var table = document.getElementById('salaries');
var rowCount = 1;

function newRowTemplate(rowCount) {
  var temp = document.createElement('table');
  temp.innerHTML = `<tr id='row_${rowCount}'>
                <td>
                    <select>
                        <option value="T1.0">Teacher 1.0</option>
                        <option value="T1.1">Teacher 1.1</option>
                        <option value="T1.2">Teacher 1.2</option>
                        <option value="T1.3">Teacher 1.3</option>
                    </select>
                </td>
                <td><input type="number" name="hourlyRate" value="" onKeyUp="calc('row_${rowCount}');"></td>
                <td><input type="number" name="salaryHours" value="" onkeyUp = "calc('row_${rowCount}');"></td>
                <td><input type="number" name="salaryCost" readonly="readonly"></td>
                <td><input type="text" name="salComments"></td>
                <td><input type="text" name="salType"></td>
                <td><input type="button" value="+" ; onclick="addRow()"></td>
                <td><input type="button" value="-" ; onclick="removeRow(this)"></td>
            </tr>`;

  return temp.firstChild;
}

function addRow() {
  var newRow = newRowTemplate(rowCount);
  table.appendChild(newRow);
  rowCount += 1;
}

function removeRow(el) {
  el.parentNode.parentNode.remove();
  rowCount -= 1;
}
<body>
  <form name="vetCosting">
    <h3> Salaries </h3>
    <h3> Salaries </h3>
    <table id="salaries">
      <tr>
        <th>Classification</th>
        <th>Hourly rate</th>
        <th>Hours</th>
        <th>Cost</th>
        <th>Comments</th>
        <th>Type</th>
        <th></th>
        <th></th>
      </tr>
      <tr id="row_0">
        <td>
          <select>
            <option value="T1.0">Teacher 1.0</option>
            <option value="T1.1">Teacher 1.1</option>
            <option value="T1.2">Teacher 1.2</option>
            <option value="T1.3">Teacher 1.3</option>
          </select>
        </td>
        <td><input type="number" name="hourlyRate" value="" onKeyUp="calc('row_0');"></td>
        <td><input type="number" name="salaryHours" value="" onkeyUp="calc('row_0');"></td>
        <td><input type="number" name="salaryCost" readonly="readonly"></td>
        <td><input type="text" name="salComments"></td>
        <td><input type="text" name="salType"></td>
        <td><input type="button" value="+" ; onclick="addRow()"></td>
        <td><input type="button" value="-" ; onclick="removeRow(this)"></td>
      </tr>
    </table>
  </form>
</body>

I just realized a bug in my code. Once row_x is created, and I delete and add another row, it'll create row_x again because the rowCount returns to x. You can fix this by removing the decrement in the remove row function.

Arvind
  • 155
  • 3
  • 12
  • 1
    Thank you so much Arvind for taking the time to help and your step by step comments. Just one more question, and I'm sorry as its very basic because I am soooo new to this. Where do I put the functions? ie. within the section, the section or somewhere else? – Pan Jun 21 '19 at 06:30
  • 1
    Got it working, thank you so much. I am so grateful for your time and patience in helping with this issue. Cheers – Pan Jun 21 '19 at 06:45
  • You're welcome! You figured out where to add the functions? – Arvind Jun 21 '19 at 08:06
  • Hi Arvind. I am using the same code you provided as reference for another table with rows, but the rows are not calculating across. Any idea why that would be? – Pan Jun 27 '19 at 05:22
  • Hey. I'm not sure what you meant? You now want to add and remove rows instead, and calculate a sum down a column? – Arvind Jun 27 '19 at 08:39