Calculation results not displaying in totals table javascript - javascript

I'm trying to create a paycheck calculator, but when I enter the hourly rate and number of hours and hit calculate, all I'm getting is undefined in my totals table. Here is my javascript:
var $ = function (id) {
return document.getElementById(id);
}
function updateTotal () {
var rate;
var hours = parseFloat( $("numHours").value);
var regularHours;
var overtime;
var doubletime;
//begin determine hourly rate
function checkJob () {
if ($('job').value == 'job0') {
rate = 0;
}
if ($('job').value == 'job1') {
rate = 12;
}
if ($('job').value == 'job2') {
rate = 10;
}
if ($('job').value == 'job3') {
rate = 11;
}
}
//calculate hours breakdown
function checkHours () {
if (hours.value <= 40) {
regularHours = hours.value;
overtime = 0;
doubletime = 0;
}
if (hours.value < 60) {
regularHours = 40;
overtime = hours.value-40;
doubletime=0;
}
if (hours.value >=60) {
regularHours = 40;
overtime = 20;
doubletime = hours.value-60;
}
}
checkJob();
checkHours();
var salary = (regularHours * rate) + (overtime * (rate * 1.5)) + (doubletime * (rate * 2))
//display amounts
$('regularHours').innerHTML = regularHours;
$('overtime').innerHTML = overtime;
$('doubletime').innerHTML = doubletime;
$('salary').innerHTML = "$ " + salary;
}
I'm sure I'm missing something silly, but I've been staring at this code for days and can't figure out what I'm doing wrong. Thanks for looking!

EDIT
The offending code is this:
hours.value
The problem with it is that hours is already a number, and number does not have the method value available to it. Changing all instances of hours.value to hours will solve the problem and properly output a calculated result.
See this demo: http://jsfiddle.net/gLthv/
The problem is the way you are implementing your jquery selectors. You should either use document.getElementById() instead, or remember that to select an id, you need to prefix the id name with a #
$("#id")
That is only the first part of the problem though. The next part is that you are using jquery, but expecting dom elements. To unwrap the jquery object, you need to use [0] with it to be able to access the contained dom element.
$("#id")[0]
Once you make these changes, you should be able to see the reflected calculations. For example, $('job').value should really be $('#job')[0].value or if you prefer to stick with jquery's implementation then you can use $('#job').val() to get the value.
Similar to this you may also access jquery's implementation of innerHTML. That would change this: $('overtime').innerHTML = overtime to $('#overtime').html(overtime)

Related

calculating an equation given a certain input from a user with JavaScript

JavaScript newbie here.
I am tasked with calculating a user's monthly payment by using a given equation. I am having trouble with getting the values from the user.
const loanAmount = document.getElementById('loan-amount');
const loanYears = document.getElementById('loan-years');
const loanRate = document.getElementById('loan-Rate');
const span = document.querySelector('span');
const form = document.getElementById("calc-form");
form.addEventListener("submit", function (e) {
e.preventDefault();
console.log('hello')
makeLogo();
});
function makeLogo(loanAmount, loanYears, loanRate) {
const principle = loanAmount.value
const n = loanYears.value * 12;
const i = loanRate.value / 12;
const monthylPayment = (principle* i)/1-(1+ i)** -(n);
span.innerText = monthylPayment;
}
This is what I have so far and am getting an error for the variables in the makeLogo function.
It's a good idea to separate your inputs, calculations and rendering into separate functions. try to keep functions as simple as possible.
You will need to re-evaluate your monthly cost calculator, but here is a working example which takes input, calculates and then renders into form fields.
document.getElementById("calc-form").addEventListener('submit', (e) => {
e.preventDefault();
var loanAmount = document.getElementById('loan-amount').value;
console.log(loanAmount);
var loanYears = document.getElementById('loan-years').value;
var loanRate = document.getElementById('loan-rate').value;
var monthlyPayment = makeLogo( loanAmount, loanYears, loanRate );
console.log(monthlyPayment);
// the monthly has now been calculated, simply put it where you'd like
var calculated = document.getElementById('calculated');
calculated.value = monthlyPayment;
var totalRepayment = document.getElementById('totalRepayment');
totalRepayment.value = monthlyPayment * ( loanYears * 12 );
} );
function makeLogo( principle, loanYears, loanRate) {
var n = loanYears * 12;
var i = loanRate / 12;
var result = ( principle * i) / 1 - ( 1 + i )**-( n );
return result;
}
<html>
<form action='submit' id ='calc-form'>
Loan Amount:<input id ='loan-amount'></input><BR/>
Loan Years:<input id='loan-years'></input><BR/>
Loan Rate:<input id='loan-rate'></input><BR/>
<input type='submit'>
</form>
<span id='span-output'>
Monthly Payment :<input id='calculated' readonly><BR/>
Total Re-Payment :<input id='totalRepayment' readonly>
</span>
</html>
The error you are seeing is likely because the makeLogo function is trying to access the value property of the loanAmount, loanYears, and loanRate variables. Still, they are DOM elements and not their values.
You can fix this by accessing the value property of the DOM elements before passing them to the function like so:
form.addEventListener("submit", function (e) {
e.preventDefault();
console.log('hello')
const principle = loanAmount.value;
const n = loanYears.value * 12;
const i = loanRate.value / 12;
makeLogo(principle, n, i);
});
function makeLogo(principle, n, i) {
const monthylPayment = (principle* i)/1-(1+ i)** -(n);
span.innerText = monthylPayment;
}
This way, the makeLogo function receives the values of the input fields as arguments and can perform the calculation correctly.
Also, make sure that you are getting the right input from the user by checking the value of each element by doing the following:
console.log(loanAmount.value,loanYears.value,loanRate.value)
and check if they are the values that you are expecting.

Using math.random in button to set a select option?

I'm trying to make a button that randomly sets a select option when clicked. I can't figure out what I am doing wrong though...
http://jsfiddle.net/GamerGorman20/nw8Ln6ha/25/
$('#rand').click(
function() {
randNum();
var num = "";
document.getElementById("mySelect").value.innerHTML = favWebComics[num];
});
var randNum = function() {
num = Math.round(Math.random() * 2) + 1;
return num;
};
The shown code is only a small part of the larger script housed in the linked jsfiddle.
I plan to add more selections later, but I want to get the code figured out before I spend time on those.
Worth mentioning, my understanding of this is very limited, so keeping the advice simple is GREATLY appreciated.
The relevant part of your code that you will need to change will look like this when complete (see the updated jsfiddle):
$('#rand').click(function() {
var $select = document.getElementById('mySelect'),
max = $select.getElementsByTagName('option').length - 1;
$select.selectedIndex = randNum(1, max);
myFunction();
});
var randNum = function(min, max) {
num = Math.floor(Math.random() * (max - min + 1)) + min;
return num;
};
var myFunction = function() {
var x = document.getElementById("mySelect").value;
document.getElementById("demo").innerHTML = "You selected: " + x;
document.getElementById("image").innerHTML = favWebComics[x][1];
document.getElementById("web").innerHTML = favWebComics[x][0];
document.getElementById("tags").innerHTML = "Tags: " + favWebComics[x][2];
};
I haven't changed the style or structure of your code, but just some of the basic properties.
The problem you have is with innerHTML. You cannot set innerHTML on an element's value.
Instead, what you can do is generate a random number and set the selectedIndex property of the select element to that random number.
Then, you'll call the function that displays the images and whatnot that you need.
You cannot change the select with innerHTML like this:
document.getElementById("mySelect").value.innerHTML = favWebComics[num];
You have to instead change the value, and apply the change like so
$("#mySelect").val("New text").change();
or
$("#mySelect").prop('selectedIndex', index).change();

Javascript rounding failure

This is the rounding function we are using (which is taken from stackoverflow answers on how to round). It rounds half up to 2dp (by default)
e.g. 2.185 should go to 2.19
function myRound(num, places) {
if (places== undefined) {
// default to 2dp
return Math.round(num* 100) / 100;
}
var mult = Math.pow(10,places);
return Math.round(num* mult) / mult;
}
It has worked well but now we have found some errors in it (in both chrome and running as jscript classic asp on IIS 7.5).
E.g.:
alert(myRound(2.185)); // = 2.19
alert (myRound(122.185)); // = 122.19
alert (myRound(511.185)); // = 511.19
alert (myRound(522.185)); // = 522.18 FAIL!!!!
alert (myRound(625.185)); // = 625.18 FAIL!!!!
Does anyone know:
Why this happens.
How we can round half up to 2 dp without random rounding errors like this.
update: OK, the crux of the problem is that in js, 625.185 * 100 = 62518.499999
How can we get over this?
Your problem is not easily resolved. It occurs because IEEE doubles use a binary representation that cannot exactly represent all decimals. The closest internal representation to 625.185 is 625.18499999999994543031789362430572509765625, which is ever so slightly less than 625.185, and for which the correct rounding is downwards.
Depending on your circumstances, you might get away with the following:
Math.round(Math.round(625.185 * 1000) / 10) / 100 // evaluates to 625.19
This isn't strictly correct, however, since, e.g., it will round, 625.1847 upwards to 625.19. Only use it if you know that the input will never have more than three decimal places.
A simpler option is to add a small epsilon before rounding:
Math.round(625.185 * 100 + 1e-6) / 100
This is still a compromise, since you might conceivably have a number that is very slightly less than 625.185, but it's probably more robust than the first solution. Watch out for negative numbers, though.
Try using toFixed function on value.
example is below:
var value = parseFloat(2.185);
var fixed = value.toFixed(2);
alert(fixed);
I tried and it worked well.
EDIT: You can always transform string to number using parseFloat(stringVar).
EDIT2:
function myRound(num, places) {
return parseFloat(num.toFixed(places));
}
EDIT 3:
Updated answer, tested and working:
function myRound(num, places) {
if (places== undefined) {
places = 2;
}
var mult = Math.pow(10,places + 1);
var mult2 = Math.pow(10,places);
return Math.round(num* mult / 10) / mult2;
}
EDIT 4:
Tested on most examples noted in comments:
function myRound(num, places) {
if (places== undefined) {
places = 2;
}
var mult = Math.pow(10,places);
var val = num* mult;
var intVal = parseInt(val);
var floatVal = parseFloat(val);
if (intVal < floatVal) {
val += 0.1;
}
return Math.round(val) / mult;
}
EDIT 5:
Only solution that I managed to find is to use strings to get round on exact decimal.
Solution is pasted below, with String prototype extension method, replaceAt.
Please check and let me know if anyone finds some example that is not working.
function myRound2(num, places) {
var retVal = null;
if (places == undefined) {
places = 2;
}
var splits = num.split('.');
if (splits && splits.length <= 2) {
var wholePart = splits[0];
var decimalPart = null;
if (splits.length > 1) {
decimalPart = splits[1];
}
if (decimalPart && decimalPart.length > places) {
var roundingDigit = parseInt(decimalPart[places]);
var previousDigit = parseInt(decimalPart[places - 1]);
var increment = (roundingDigit < 5) ? 0 : 1;
previousDigit = previousDigit + increment;
decimalPart = decimalPart.replaceAt(places - 1, previousDigit + '').substr(0, places);
}
retVal = parseFloat(wholePart + '.' + decimalPart);
}
return retVal;
}
String.prototype.replaceAt = function (index, character) {
return this.substr(0, index) + character + this.substr(index + character.length);
}
OK, found a "complete" solution to the issue.
Firstly, donwnloaded Big.js from here: https://github.com/MikeMcl/big.js/
Then modified the source so it would work with jscript/asp:
/* big.js v2.1.0 https://github.com/MikeMcl/big.js/LICENCE */
var Big = (function ( global ) {
'use strict';
:
// EXPORT
return Big;
})( this );
Then did my calculation using Big types and used the Big toFixed(dp), then converted back into a number thusly:
var bigMult = new Big (multiplier);
var bigLineStake = new Big(lineStake);
var bigWin = bigLineStake.times(bigMult);
var strWin = bigWin.toFixed(2); // this does the rounding correctly.
var win = parseFloat(strWin); // back to a number!
This basically uses Bigs own rounding in its toFixed, which seems to work correctly in all cases.
Shame Big doesnt have a method to convert back to a number without having to go through a string.

Strange behavior when dividing inputs

I am building a basic application to learn more, but have came across an issue.
I have 3 input boxes. people, bill, tip. the maths is as follows:
(bill + tip) / people. When i try to divide in my code it seems to add onto the end of my total.
So far i have this. http://jsfiddle.net/ma9ic/a8eJT/
var updateTotal = function () {
var people = parseInt($('#people').val());
var bill = ($('#bill').val());
var tip = ($('#tip').val());
var billTip = bill + tip;
var billTipPeople = billTip / people;
$('#total').text("£" + billTipPeople)
If i could get pointed in the right direction that would be great :)
You're pretty close. I got it working like this
var updateTotal = function () {
var people = parseInt($('#people').val(),10);
var bill = parseFloat($('#bill').val());
var tip = parseFloat($('#tip').val());
var billTip = bill + tip;
var billTipPeople = billTip / people;
if (isNaN(billTipPeople)) billTipPeople = 0; // output zero if NaN
$('#total').text("£" + billTipPeople.toFixed(2))
The issue is that javascript has some weird rules about string concatenation. "1"+"1" == "11". You need to be explicit every time.
parseInt GOTCHA: ALWAYS use the second (optional) base parameter of parseInt. Values like "015" will be parsed as octal into the decimal number 13 otherwise. Hence the popular joke, "Why do programmers confuse Halloween and Christmas? Because OCT31 == DEC25!"
bill + tip will perform string concatenation, not addition, because they are both string. At least one of the operands has to be a number if you want to perform addition.
While parseFloat and parseInt work, using the unary plus operator is shorter to write and you don't have to worry about the type of number:
var people = +$('#people').val();
var bill = +$('#bill').val();
var tip = +$('#tip').val();
This works as long as the input value only consists of a number. But if the input only starts with a number, e.g. "5 foo" and you want to extract the number from the beginning of the string, you really have to use parseInt or parseFloat.
Your bill and tip variables are strings. Try using parseFloat(). Using the addition sign (+) on two strings will simply concatenate them.
var bill = parseFloat($('#bill').val());
var tip = parseFloat($('#tip').val());
While you used parseInt() in one case, why didn't you follow same path in similar situations?
Anyway, here's how I have modified your code:
$(document).ready(function () {
$('input#bill, input#tip').blur(function () {
var num = parseFloat($(this).val());
if (isNaN(num)) {
return;
}
var cleanNum = num.toFixed(2);
$(this).val(cleanNum);
if (num / cleanNum < 1) {}
});
$('#people, #bill, #tip').keyup(function () {
updateTotal();
});
var updateTotal = function () {
var people = parseInt($('#people').val());
if (isNaN(people) || people === 0) {
return;
}
var bill = parseFloat($('#bill').val());
if (isNaN(bill)) {
bill = 0;
}
var tip = parseFloat($('#tip').val());
if (isNaN(tip)) {
tip = 0;
}
var billTip = bill + tip;
var billTipPeople = billTip / people;
$('#total').text("£" + billTipPeople);
// round up to 2 d.p. like below:
// $('#total').text("£" + billTipPeople.toFixed(2));
};
});
To save you from coming back again and asking why your app has decided to come up another crazy behaviour, I've added the following checks:
When user enters number of people, even though we are not ready to enter total cost of bill, it is updated as NaN. We prevent this nasty behaviour by...
if (isNaN(num)) {
return;
}
We take same precaution in the updateTotal() function. Moreover, watch out for division by 0!* I give tips onle when I'm with my gf, otherwise, a person like me will break your app...
if (isNaN(people) || people === 0) {
return;
}
Here's the fiddle >> http://jsfiddle.net/a8eJT/11/
Try to use parseInt() and parseFloat() in javascript.When you did with out using these then it will treat as strings.
try
var updateTotal = function () {
var people = parseInt($('#people').val());
var bill = parseInt($('#bill').val());
var tip = parseInt($('#tip').val());
var billTip = bill + tip;
var billTipPeople = billTip / people;
$('#total').text("£" + billTipPeople)
I have added parseint to both bill & tip

Incrementing a number smoothly with a variable time period in JS

I have a really simple JS counter which I display on a dashboard like screen which does the following:
Every 5 minutes it makes an jsonp call and retrieves a "total" number
It then displays this number to the screen by incrementing the last total displayed till it is equal to the new total. (the number can only ever increase)
I'm having some trouble with making the number increment smoothly. What I would like to do is find a delta (i.e. New total - old total) and increment the number gradually over the 5 minutes till the next call so it looks like a nice smooth transition.
Any ideas on how I can do this?
Currently some of my code looks like this (This block get's called every 5mins. And yes, it's in dire need of a refactor...)
var LAST_NUMBER_OF_SESSIONS = null;
var five_minutes_in_seconds = 300;
var new_number_of_sessions;
$.getJSON('http://blah.com/live_stats/default_jsonp.aspx?callback=?', function(data) {
if(LAST_NUMBER_OF_SESSIONS === null){
LAST_NUMBER_OF_SESSIONS = data.total_sessions;
}
new_number_of_sessions = data.total_sessions;
var delta = Math.floor(new_number_of_sessions - LAST_NUMBER_OF_SESSIONS);
var time_interval = (five_minutes_in_seconds / delta) * 1000;
var old_value = LAST_NUMBER_OF_SESSIONS;
var new_value = null;
sessions_interval = setInterval(function (){
new_value = parseInt(old_value, 10) + 1;
$('#stats').text(new_value);
old_value = new_value;
if(new_value >= new_number_of_sessions){
clearInterval(sessions_interval);
}
}, time_interval);
LAST_NUMBER_OF_SESSIONS = new_value;
});
}
This code it seems to increment the number very quickly at the start of the 5min period and then stop so it's not exactly right...
Try this:
var total = 0,
delta = 0,
stats = $('#stats').text( total );
function increment() {
var v = +stats.text();
if ( v < total ) {
stats.text( v + 1 );
} else {
$.getJSON('http://...', function(data) { // added data here
delta = Math.floor( 300000 / ( data.total_sessions - total ) );
total = data.total_sessions;
});
}
setTimeout(increment, delta);
}
Update:
In order to test my code, I had to simulate the JSON reponse - I used an array of numbers. See here: http://jsfiddle.net/simevidas/MwQKM/
(In the demo, I use an interval of 5 seconds instead of 5 minutes.)
I am not exactly sure why your code doesn't work as expected, although I suspect that it has to do with line LAST_NUMBER_OF_SESSIONS = new_value;. I wrote something similar and it works fine. It's not that different from what you have, minus that last line of code.

Categories

Resources