1

I want to create a tiered pay calculator utility.

In my Javascript file I dynamically add rows and fields to a table I created with the HTML from a list of names I have in a text input field. The rows consist of a name label column, a text input column to put they're gross earnings in and a calculated pay output column.

I would like for it to update the calculated pay column automatically when I make a change in the text input in the gross earnings column. Although I can trace the element ids through to the calculatePay function I can't seem to use or set their properties. I get the feeling they are not unique as well. Any ideas?

BTW you have to click the update button right now to run the Javascript.

Edit - made some changes per suggestion. Still can't seem to take variables passed to the calculatePay function and simply turn them around and spit them back out into the Pay column of my table.

Edit - SOLVED. The issue was closure, which I didn't understand at first but here's the skinny. To isolate the scope of the variables so they don't get re-wrote every time the loop comes around they need to be declared inside a function that gets recreated with every loop iteration (because Javascript scopes to the function instead of to the code block). Things to note - this does not work if you just declare a nameless function in the middle of your loop. You must return it to a variable(i.e. var buildElement = function(){Yada Yada}();). Also, after the function add () to execute it.

Javascript

function buildTable(){
  var artists = document.getElementById("artlist").value;
  var names = artists.split(",");
  var len = names.length;
  var ptable = document.getElementById("payTable");
  var rowLength = ptable.rows.length;
  for (i=0 ; i < len; i++){
   var buildElement = function(){
     var row = ptable.insertRow(rowLength);
     var nameCell = row.insertCell(0);
     var grossCell = row.insertCell(1);
     var payCell = row.insertCell(2);
     var grossText = document.createElement("input");
     grossText.type = "type";
     grossText.name = "gtext[]";
     grossText.id = "gross" + names[i];
     payCell.id = "pay" + names[i];
     grossText.onchange = function(){calculatePay(payCell.id, grossText.id);};
     grossCell.appendChild(grossText);
     nameCell.innerHTML = names[i];
    }();
  }
}
function resetTable(){
     var ptable = document.getElementById("payTable");
     var rowLength = ptable.rows.length;
     if (rowLength>2){

     for (p=rowLength; p>2; p--){
      ptable.deleteRow(2);
}
}
  buildTable();
}
    function calculatePay(target, gross) {
document.getElementById(target).innerHTML = document.getElementById(gross).value;
        }

HTML

  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title></title>
    <script src="./js/script.js"></script>
    <link rel="stylesheet" href="./css/style.css" />
  </head>
  <body>
    <div>
      <table id="payTable">
        <tr>
        <th colspan=3 class="hdr">
        Calculator
        </th>
        </tr>
        <tr>
        <th class="hdr">
        Name
        </th>
        <th class="hdr">
        Gross
        </th>
        <th class="hdr">
        Pay
        </th>
        </tr>
      </table>
    </div>

    <div>
      <table>
        <tr>
        <th colspan=3 class="hdr">
        SETTINGS
        </th>
        </tr>
        <tr>
        <th colspan=3 class="hdr">
        Breakpoints
        </th>
        </tr>
        <tr>
        <td>Break 1 at $
        <input type="text" name="break1"   id="break1" value="300"/>
        </td>
        <td>
        </td>
        <td>Break 2 at $
        <input type="text" name="break2" id="break2" value="900"/>
        </td>
        </tr>
        <tr>
        <th colspan=3 class="hdr">
        Percentage levels
        </th>
        </tr>
        <tr>
        <td>
        Below break 1:
        <input type="text" size="4" name="per1" id="per1" value="50"/>%
        </td>
        <td>
        Between breaks 1 and 2:
        <input type="text" size="4" name="per2" id="per2" value="60"/>%
        </td>
        <td>
        Over break 2:
        <input type="text" size="4" name="per3" id="per3" value="70"/>%
        </td>
        </tr>
        <tr>
        <td>
        Artists:
        <input type="text" name="artlist" id="artlist" value="Brian,Eric,Christie,Cynthia,Shawn"/>
        </td>
        <td>
        </td>
        <td class="hdr">
        <button onclick="resetTable()">Update</button>
        </td>
      </tr>

    </div>
  </body>
</html>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • Tip: Please use paragraphs. It makes things easier to read. – emerson.marini Aug 25 '14 at 21:50
  • Could you post your HTML so I could test the snippet above. – hex494D49 Aug 25 '14 at 21:56
  • voted to close - see help: Include just enough code to allow others to reproduce the problem. For help with this, read How to create a Minimal, Complete, Valid Example. can't do much without a working example. – lincolnk Aug 25 '14 at 22:11
  • @MelanciaUK please take another look. I made some edits. – Eric Hylland Aug 26 '14 at 01:12
  • @lincolnk please take another look. I made some edits. – Eric Hylland Aug 26 '14 at 01:17
  • Well, change this `grossText.onchange = calculatePay(payCell.id, grossText.id);` to this `grossText.onchange = function(){ calculatePay(payCell.id, grossText.id);};` and let me know if it helped. – hex494D49 Aug 26 '14 at 01:38
  • @hex494D49 that did clear up a lot and allowed it to follow through with generating the table. Why do you need to create a function just to call another function? Anyway the real blockage is in the calculatePay function. Even when I replace everything in it with a simple get and set I get no result in the pay column. I replaced it with this just to make it super simple: 'document.getElementById("target").innerHTML = document.getElementById("gross").value;' – Eric Hylland Aug 26 '14 at 03:43
  • One problem I see is that ids need to be unique but you are generating the same id in the for loop. Also, in your `calculatePay` function you want to drop the quotes e.g. `getElementById(target)`. And use some `console.log(x)` to see some debug output to verify your variables contain values you are expecting. – Jasen Aug 26 '14 at 17:48
  • @Jasen ok, so I'm generating unique id's and I've made the other changes you suggested but now I'm running into a closure issue when assigning the names to the elements. All element ids are payShawn and grossShawn. – Eric Hylland Aug 27 '14 at 11:55
  • 1
    Do not keep you solution in the Question post... Id recommend you answering your own question with the solution you got so it gets easier for other people to see it :) – Felipe Skinner Aug 27 '14 at 17:12
  • possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) –  Sep 01 '14 at 01:50

1 Answers1

0

The issue was closure, which I didn't understand at first but here's the skinny. To isolate the scope of the variables so they don't get re-wrote every time the loop comes around they need to be declared inside a function that gets recreated with every loop iteration (because javascript scopes to the function instead of to the code block). Things to note - this does not work if you just declare a nameless function in the middle of your loop. You must return it to a variable(i.e. "var buildElement = function(){Yada Yada}();"). Also, after the function add () to execute it.

Javascript

function buildTable(){
  var artists = document.getElementById("artlist").value;
  var names = artists.split(",");
  var len = names.length;
  var ptable = document.getElementById("payTable");
  var rowLength = ptable.rows.length;
  for (i=0 ; i < len; i++){
   var buildElement = function(){
     var row = ptable.insertRow(rowLength);
     var nameCell = row.insertCell(0);
     var grossCell = row.insertCell(1);
     var payCell = row.insertCell(2);
     var grossText = document.createElement("input");
     grossText.type = "type";
     grossText.name = "gtext[]";
     grossText.id = "gross" + names[i];
     payCell.id = "pay" + names[i];
     grossText.onchange = function(){calculatePay(payCell.id, grossText.id);};
     grossCell.appendChild(grossText);
     nameCell.innerHTML = names[i];
    }();
  }
}
function resetTable(){
     var ptable = document.getElementById("payTable");
     var rowLength = ptable.rows.length;
     if (rowLength>2){

     for (p=rowLength; p>2; p--){
      ptable.deleteRow(2);
}
}
  buildTable();
}
    function calculatePay(target, gross) {
document.getElementById(target).innerHTML = document.getElementById(gross).value;
        }

HTML

  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title></title>
    <script src="./js/script.js"></script>
    <link rel="stylesheet" href="./css/style.css" />
  </head>
  <body>
    <div>
      <table id="payTable">
        <tr>
        <th colspan=3 class="hdr">
        Calculator
        </th>
        </tr>
        <tr>
        <th class="hdr">
        Name
        </th>
        <th class="hdr">
        Gross
        </th>
        <th class="hdr">
        Pay
        </th>
        </tr>
      </table>
    </div>

    <div>
      <table>
        <tr>
        <th colspan=3 class="hdr">
        SETTINGS
        </th>
        </tr>
        <tr>
        <th colspan=3 class="hdr">
        Breakpoints
        </th>
        </tr>
        <tr>
        <td>Break 1 at $
        <input type="text" name="break1"   id="break1" value="300"/>
        </td>
        <td>
        </td>
        <td>Break 2 at $
        <input type="text" name="break2" id="break2" value="900"/>
        </td>
        </tr>
        <tr>
        <th colspan=3 class="hdr">
        Percentage levels
        </th>
        </tr>
        <tr>
        <td>
        Below break 1:
        <input type="text" size="4" name="per1" id="per1" value="50"/>%
        </td>
        <td>
        Between breaks 1 and 2:
        <input type="text" size="4" name="per2" id="per2" value="60"/>%
        </td>
        <td>
        Over break 2:
        <input type="text" size="4" name="per3" id="per3" value="70"/>%
        </td>
        </tr>
        <tr>
        <td>
        Artists:
        <input type="text" name="artlist" id="artlist" value="Brian,Eric,Christie,Cynthia,Shawn"/>
        </td>
        <td>
        </td>
        <td class="hdr">
        <button onclick="resetTable()">Update</button>
        </td>
      </tr>

    </div>
  </body>
</html>
andrew
  • 9,313
  • 7
  • 30
  • 61