7
\$\begingroup\$

Update: revised code.

I work at a Starbucks and one of the things that screams at me week after week for automation is the tedious calculation of the tip distribution. So here is my attempt to provide a simple tool for this. (Previous attempt required special software and special instruction to explain how it works.)

It has fields for inputting the number of hours for each partner and the amount of money to be dispersed. Clicking "calculate" produces the output with blank lines to help orient the output with the input rows.

As a javascript noob, I feel certain I've done some stupids in here I can learn from.

<html>
  <head>
    <title>Tip Calculator</title>
  </head>

  <body onload="loaded()">
    <h3>Tip Calculator</h3>
    Hours:<br>
    <table id=inputs>
    </table>

    <table id=controls>
      <tr>
        <td><input type=button value="add row" onclick="add_row()">
        <td>Dividend: <input id=dividend type=text size=5 maxlength=5>
        <td><input type=button value="calculate" onclick="calc()">
      </tr>
    </table>

    <span id=output colspan=3>&nbsp;</span>

  </body>
  <script language="JavaScript">
  <!--

var num_inputs = 6;

function loaded() {
  add_row();
}

function add_row(){
  var table = E('inputs');

  var new_row = table.insertRow( table.rows.length );

  for(  var i=0;  i<6;  i++) {
    new_row.insertCell(i).innerHTML = "<input type=text size=5 maxlength=5>";
  }
}

function calc(){
  var sum = 0;
  var inputs = E('inputs').getElementsByTagName('input');

  for(  var i=0;  i < inputs.length;  i++  ){
    sum += Number(inputs[i].value);
  }
  rate = E('dividend').value / sum;
  E('output').innerHTML = "Total Hours: " + sum + "<br>";
  E('output').innerHTML += "Rate (Dividend/Total Hours): " + rate + "<br>";
  for(  var i=0;  i < inputs.length;  i++  ){
    if(  (i % 6) == 0  )  E('output').innerHTML += "<br>";
    share = Number( inputs[i].value ) * rate;
    E('output').innerHTML += inputs[i].value + " &times; " + rate + " = " + share + "<br>";
  }
}

function E( id ){
  return document.getElementById( id );
}

  //-->
  </script>

</html>

screen snip

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I took a stab at this just for fun with a framework I'm learning (vue.js): codepen.io/imjosh/pen/ZKvKGx?editors=1010 \$\endgroup\$
    – imjosh
    Commented May 8, 2017 at 23:50
  • \$\begingroup\$ Should suppress the bogus output lines which correspond to empty inputs. \$\endgroup\$ Commented Nov 11, 2017 at 1:15

1 Answer 1

6
\$\begingroup\$

What you could do first is to separate out the logic that does the calculation from the logic that handles the UI. This makes it easier to update the math. Also, this makes the logic portable, should you scrap the UI code for something else, like if you move to use a framework.

Also, break up the logic to small functions that accept a predictable input. For instance, calculating shares is simply multiplying your inputs by your dividend - an array of input values and a number.

// In goes an array of hours and the dividend, out comes an array of shares
function getShares(hours, dividend){
  const sum = hours.reduce((c, v) => c + v);
  const rate = dividend / sum;
  const shares = hours.map(hour => hour * rate);
  return shares;
}

I also notice this strange E function which is just short for document.getElementById. There is document.querySelector and document.querySelectorAll that allows you to use CSS selectors to get a reference of your elements. Also, I'd avoid IDs since IDs should only appear once on the page. You can't use it for multiple elements of the same kind, for instance your inputs. Use classes instead.

For this case, I would tolerate the innerHTML usage. Otherwise, construct elements using document.createElement. Now to create repeated HTML, you can use array.map and array.join with template literals.

function renderResults(results){
  return results.map(result => `
    <div class="result">${result.hours} x ${result.dividend} = ${result.share}</div>
  `).join('');
}

For your HTML, while there are cases where the quotes on attributes are optional, it's best you put them in for consistency.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.