Olu Adabonyan
Olu Adabonyan

Reputation: 93

Sum up cells in a dynamic table

I have set up a table using javascript. Note that I have reduced the number of rows to just one for ease. After loading, user will input values in the cells. There are 3 cells on each row. When a value is typed in either cell1 or cell2, cell3 should be updated automatically. The formula is

cell1(in column: Score1) + cell2(in column: Score2) = cell3(column: Total)

However this is not working. I keep getting NaN in cell3. Can anyone help me with this?

<!DOCTYPE html>
<html>
<head>
<title>MP</title>

<style type="text/css">
th {font-weight: bold; text-align: left; font-size: 14px; border: 1px; 
border-style: solid; margin: 0px; border-colapse: colapse; 
border-spacing: 0px;}
td {font-weight: Normal; font-size: 14px; border: 1px; border-style: solid; 
margin: 0px; border-colapse: colapse;}
.sn {text-align: right;}
.fin {text-align: right;}

</style>

</head>
<body>

<table id="pr">
<tbody>
<tr class="thd">
<th>Score A</th>
<th>Score B</th>
<th>Total</th>
</tr>

<tr>
<td><input type="text" class="fin" onkeyup="update();" /></td>
<td><input type="text" class="fin" onkeyup="update();" /></td>
<td input type="text" class="fin" name="totdeduction"></td>
</tr>

</tbody>
</table>
<br />
<br />

<script>
function update() {
var j = document.getElementById("pr").rows[1].cells[0].innerHTML;
var k = parseInt(j);
var l = document.getElementById("pr").rows[1].cells[1].innerHTML;
var m = parseInt(l);
var n = (k + m);

if (j ="") {k = 0;}
else if (l ="") {m = 0;}
document.getElementById("pr").rows[1].cells[2].innerHTML = n;
}
</script>
</body>
</html>

Upvotes: 1

Views: 6554

Answers (3)

enhzflep
enhzflep

Reputation: 13109

Since you've got a number of rows to perform the same operation on, you'll need to go about this slightly differently than you would if there was only a single row.

The easiest way would probably be to simply loop through all of the rows in the table whenever a keyup event is fired, and update every single row. Obviously, this is inefficient - imagine having 1,000,000 rows! A single keystroke would cause all 1,000,000 of them to be recalculated. :eek:

The other way would be to use a function which would calculate the result for any 1 single row. You would then call this function any time a keyup event occurred, but in this case you'd only be calculating 1 row at a time. This approach is clearly superior to the former, but it comes at the expense of making use of techniques a little more advanced. Namely, the use of the .addEventListener function and either a bucket-load of inline javascript, or the understanding of the this keyword. My personal preference would be for the .addEventListener/this keyword combo.

First, here's a function that will replace the one you already have - this will calculate the third cell in each row for an unlimited number of rows.

function update()
{
    var tableElem = document.getElementById('pr');
    var rowElems = tableElem.getElementsByTagName('tr');
    var i, nRows;

    nRows = rowElems.length;
    for (i=1; i<nRows; i++)     // start at 1, since the first row (index 0) contains the table's header
    {
        var curRowInputs = rowElems[i].getElementsByTagName('input');

        var firstVal = parseInt(curRowInputs[0].value);
        var secondVal = parseInt(curRowInputs[1].value);

        if (isNaN(firstVal) == true)
            firstVal = 0;

        if (isNaN(secondVal) == true)
            secondVal = 0;

        var result = firstVal + secondVal;
        curRowInputs[2].value = result;
    }
}

By using the isNaN function, we ensure that the entered text is in fact a number. If it isn't we simply treat the input as though it were 0.

------------------------------------------------------------

Now for the second approach. I'll use different html for this one, since I dont need (or want!) any of the inline javascript. We'll also need to initialize the event handlers before use too.

First, the new html for the table, which is the same as yours with a few exceptions.

  1. There are now 2 rows on which to perform calculations.
  2. The inline JS is gone.
  3. The name attribute has been removed for the sake of a short example.
  4. The input in the third cell has the readonly attribute applied. We don't want the user typing in their own result - we want to ensure it is calculated.

Html

<table id="pr">
    <tbody>
        <tr class="thd">
            <th>Score A</th>
            <th>Score B</th>
            <th>Total</th>
        </tr>
        <tr>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin' readonly /></td>
        </tr>
        <tr>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin' readonly /></td>
        </tr>
    </tbody>
</table>

Next we'll need a function that will initialize all of the event handlers for us.

function addTableEventHandlers()
{
    var tableElem = document.getElementById('pr');
    var rowElems = tableElem.getElementsByTagName('tr');
    var i, nRows;

    nRows = rowElems.length;
    for (i=1; i<nRows; i++)     // start at 1, since the first row (index 0) contains the table's header
    {
        var curRowInputs = rowElems[i].getElementsByTagName('input');
        curRowInputs[0].addEventListener('keyup', myOnKeyupFunc, false);
        curRowInputs[1].addEventListener('keyup', myOnKeyupFunc, false);
    }
}

Following this, we want a function that will perform the actual calculations.

function myOnKeyupFunc(evt)
{
    var inputThatFiredTheEvent = this;
    var containingCell = inputThatFiredTheEvent.parentNode;
    var containingRow = containingCell.parentNode;
    var inputsInRow = containingRow.getElementsByTagName('input');

    var firstVal = parseInt(inputsInRow[0].value);
    var secondVal = parseInt(inputsInRow[1].value);

    if (isNaN(firstVal) == true)
        firstVal = 0;

    if (isNaN(secondVal) == true)
        secondVal = 0;

    var result = firstVal + secondVal;
    inputsInRow[2].value = result;  
}

Finally, we need a way to call the function that will initialize the table. A good time for this is after we know the document has finished loading and all of the required elements will exist and have been parsed. A good candidate for this is the onload event of the window object.

This will cause a function to be called when this has happened:

window.addEventListener('load', onDocLoaded, false);

Then, inside the onDocLoaded function which we will define in a moment, we simply make a call to addTableEventHandlers.

function onDocLoaded(evt)
{
    addTableEventHandlers();
}

Phew! Hope you're still with me here. Tying it all together, we end up with the following complete code:

<!DOCTYPE html>
<html>
<head>
<title>MP</title>
<script>
window.addEventListener('load', onDocLoaded, false);

function onDocLoaded(evt)
{
    addTableEventHandlers();
}

function addTableEventHandlers()
{
    var tableElem = document.getElementById('pr');
    var rowElems = tableElem.getElementsByTagName('tr');
    var i, nRows;

    nRows = rowElems.length;
    for (i=1; i<nRows; i++)     // start at 1, since the first row (index 0) contains the table's header
    {
        var curRowInputs = rowElems[i].getElementsByTagName('input');
        curRowInputs[0].addEventListener('keyup', myOnKeyupFunc, false);
        curRowInputs[1].addEventListener('keyup', myOnKeyupFunc, false);
    }
}

function myOnKeyupFunc(evt)
{
    var inputThatFiredTheEvent = this;
    var containingCell = inputThatFiredTheEvent.parentNode;
    var containingRow = containingCell.parentNode;
    var inputsInRow = containingRow.getElementsByTagName('input');

    var firstVal = parseInt(inputsInRow[0].value);
    var secondVal = parseInt(inputsInRow[1].value);

    if (isNaN(firstVal) == true)
        firstVal = 0;

    if (isNaN(secondVal) == true)
        secondVal = 0;

    var result = firstVal + secondVal;
    inputsInRow[2].value = result;  
}
</script>

<style type="text/css">
th {font-weight: bold; text-align: left; font-size: 14px; border: 1px; 
border-style: solid; margin: 0px; border-colapse: colapse; 
border-spacing: 0px;}
td {font-weight: Normal; font-size: 14px; border: 1px; border-style: solid; 
margin: 0px; border-colapse: colapse;}
.sn {text-align: right;}
.fin {text-align: right;}
</style>
</head>
<body>
<table id="pr">
    <tbody>
        <tr class="thd">
            <th>Score A</th>
            <th>Score B</th>
            <th>Total</th>
        </tr>
        <tr>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin' readonly /></td>
        </tr>
        <tr>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin'/></td>
            <td><input type='text' class='fin' readonly /></td>
        </tr>
    </tbody>
</table>
<br/>
<br/>
</body>
</html>

Upvotes: 1

Rob Schmuecker
Rob Schmuecker

Reputation: 8954

First off your ordering was slightly wrong. Have a look below. Also we now need to make sure this works for any rows rather than tying it down to specific rows with indexes so we pass in the this keyword into the function so we can reference the parent row and thus it's children no matter which row.

So you HTML will look like this

<table id="pr">
    <tbody>
        <tr class="thd">
            <th>Score A</th>
            <th>Score B</th>
            <th>Total</th>
        </tr>
        <tr>
            <td>
                <input type="text" class="fin" onkeyup="update(this);" />
            </td>
            <td>
                <input type="text" class="fin" onkeyup="update(this);" />
            </td>
            <td>
                <input type="text" class="fin" name="totdeduction" />
            </td>
        </tr>
    </tbody>
</table>

Your JS like this :

function isNumber(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

function update(element) {
    rob = element;
    var j = element.parentElement.parentElement.children[0].children[0].value;
    var l = element.parentElement.parentElement.children[1].children[0].value;
    console.log(j, l);

    if (j === "" || !isNumber(j)) {
        j = 0;
    }
    if (l === "" || !isNumber(l)) {
        l = 0;
    }

    var k = parseInt(j, 10);
    var m = parseInt(l, 10);
    var n = (k + m);

    element.parentElement.parentElement.children[2].children[0].value = n;
}

Which includes a function to make sure text does not affect the formula.

Demo: http://jsfiddle.net/47xs738w/

Upvotes: 0

user3714582
user3714582

Reputation: 1940

Try to check the repaired script here: http://jsfiddle.net/9p19nngr/

function update() {
    var j = document.getElementById("pr").rows[1].cells[0].getElementsByTagName("input")[0].value;
    var k = parseInt(j);
    if (isNaN(k)) {k=0}
    var l = document.getElementById("pr").rows[1].cells[1].getElementsByTagName("input")[0].value;
    var m = parseInt(l);
    if (isNaN(m)) {m=0}
    var n = (k + m);
    document.getElementById("pr").rows[1].cells[2].innerHTML = n;
}

You assumed that var j contains number, but j = document.getElementById("pr").rows[1].cells[0].innerHTML assigned to j the DOM object of input which table cell contains. Instead you should assign inputs value: j = document.getElementById("pr").rows[1].cells[0].getElementsByTagName("input")[0].value

Upvotes: 1

Related Questions