Recursive function only firing once - javascript

In the following function, I don't understand why the counter function only fires once (the figure goes up by a single increment, I want it to count up to homeFigTwo).
function effectFour() {
var homeFigOne = parseFloat($('.home .figure').text());
var homeFigTwo = 23.99;
var plusFigOne = parseFloat($('.home-plus .figure').text());
var plusFigTwo = 28.49;
var homeInc = homeFigOne < homeFigTwo ? .01 : -.01;
var plusInc = plusFigOne < plusFigTwo ? .01 : -.01;
function counterOne(){
if (homeFigOne === homeFigTwo){
return
}else{
homeFigOne = (homeFigOne + homeInc).toFixed(2);
$('.home .figure').text(homeFigOne);
window.setTimeout(counterOne, 100);
}
}
counterOne();
}
This can be seen in context here: http://codepen.io/timsig/pen/NdvBKN.
Many thanks for any help.

toFixed() has a Return value of
A string representing the given number using fixed-point notation.
This means that on the second time that this happens:
homeFigOne = (homeFigOne + homeInc).toFixed(2);
What's really going on is: "16.00" = "16.00" + 0.01 which, in fact, does not possess a toFixed method, as that whole sentence is what.
So what you want is to parseFloat the result of homeFigOne again, because whenever you toFixed it you set it to a string again.
homeFigOne = (parseFloat(homeFigOne) + homeInc).toFixed(2)

Your recursion is working as expected, but on your second call an error is thrown. This is because you convert homeFigOne to a string by using toFixed.
So it basically does this:
first call: values are 15.99 23.99 (both numbers)
second call: values are "16.00" 23.99 (a string and a number)
As the toFixed method is not defined for Strings an exception is thrown. As this happens async in a anonymous function, you prob. didn't noticed.
So my suggestion is to first make the increment, and only cast for your html element:
function effectFour() {
var homeFigOne = parseFloat($('.home .figure').text());
var homeFigTwo = 23.99;
var plusFigOne = parseFloat($('.home-plus .figure').text());
var plusFigTwo = 28.49;
var homeInc = homeFigOne < homeFigTwo ? .01 : -.01;
var plusInc = plusFigOne < plusFigTwo ? .01 : -.01;
function counterOne(){
if (homeFigOne === homeFigTwo){
return
}else{
homeFigOne = homeFigOne + homeInc;
$('.home .figure').text(homeFigOne.toFixed(2));
window.setTimeout(counterOne, 100);
}
}
counterOne();
}
edit:
+ as you are dealing with floats you are better of with >= instead of === for your end criterium

Related

Why can't println(); be used as a variable?

I'm fairly new to Javascript, and am confused on something. Why can't the command "println("..."); be called as a variable such as: var num = println("...");. I could be wrong, and if you are able to, I'd be happy to know how. But after some testing it seems like I can't. My test code is:
function start() {
var SENTINEL = "1 1";
var rollOne = Randomizer.nextInt(1, 6);
var rollTwo = Randomizer.nextInt(1, 6);
var num = println(rollOne + rollTwo);
if(num == SENTINEL) {
println("You did it");
}
}
All it's supposed to do is give to random numbers in a # # form and, if it sees that the numbers are 1,1, it will give a message. It wont give the message and can't seem to view the variable "num" as an actual variable. But when I change the variable num to simply asking the user for a number:
function start() {
var SENTINEL = -1;
var rollOne = Randomizer.nextInt(1, 6);
var rollTwo = Randomizer.nextInt(1, 6);
var num = readInt("Enter number");
if(num == SENTINEL) {
println("You did it");
}
}
And type in -1, it triggers the sentinel, thus promptly displaying the message. This is a really roundabout way to ask a simple question but I hope I can get some help. Thank you :)
Why can't the command "println("..."); be called as a variable such as: var num = println("...");
[...] It wont give the message and can't seem to view the variable
If the value returned is unusable, it is most likely undefined; i.e. The function println doesn't explicitly return anything.
In your case, you could try something like this:
var printInt = function(num) { println(num); return num; }
Note, println isn't part of the standard JavaScript language. For modern web browsers, it can be adapted to use (console.log(...)).
var printInt = function(num) { console.log(num); return num; }
And then to adapt to your code:
var num = printInt(rollOne + rollTwo);
But this still won't validate because you're comparing against "1 1" whereas your logic will return 2. JavaScript (as well as many other languages) implicitly uses addition when supplied with two numbers, but concatenation when supplied with at least one string.
var SENTINEL = "1 1"; // <---- String!
var SENTINEL = -1; // <---- Number!
So you should consider something like this instead (renamed accordingly):
var printRolls = function(text) { println(text); return text; }
var rolls = printRolls(rollOne + " " + rollTwo);
if(rolls == SENTINEL) {
println("You did it");
}
Or to simplify it a bit:
if(printRolls(rollOne + " " + rollTwo) == SENTINEL)
println("You did it");
It is possible that println doesn't return the string that is passed into. In that case, you can use
if (SENTINEL === rollOne + " " + rollTwo)
to format the string and properly test equality.
In JavaScript it is possible to assign the return value from any function to a variable similar to how you've done it:
var anyVariable = anyFunction();
But, some functions return the value undefined. Or they return a number, or an array, or...whatever.
I imagine your println() function prints the value you pass to it somewhere (on the screen? to the console?) and then returns undefined. Or if it is returning the printed value it is in a format different to what you have used in your SENTINEL variable. So then when you try to compare that with SENTINEL it won't be equal.
To fix your original function, assign the sum of the rolls to a variable, then print and test that:
function start() {
var SENTINEL = 2;
var rollOne = Randomizer.nextInt(1, 6);
var rollTwo = Randomizer.nextInt(1, 6);
var num = rollOne + rollTwo;
println(num);
if(num == SENTINEL) {
println("You did it");
}
}
EDIT: if you want the println() to display a string like "1 1" or "3 5" to show what each of the two rolls were then do this:
println(rollOne + " " + rollTwo);
That is, create a new string that is the result of concatenating rollOne's value with a single space and then rollTwo's value.

Javascript Count numbers

This probably is a very easy solution, but browsing other questions and the internet did not help me any further.
I made a javascript function which will give me a random value from the array with its according points:
function random_card(){
var rand = Math.floor(Math.random()*cards.length);
var html = "card: "+cards[rand][0]+"<br/>points: "+cards[rand][1]+"<br/><br/>";
document.getElementById("Player").innerHTML += html;
var punten = cards[rand][1];
document.getElementById("Points").innerHTML += punten;
}
I've added a += punten so i can see that it works correctly. It shows me all the point in the div with the id Points.
But what i wanted to do is count it all together so if i were to draw a 4, King and a 10 it should show 24 instead of 41010.
Thanks in advance! And if you're missing any information please let me know
Currently you are just adding strings together, which concatenate (join together) hence why you end up with 41010. You need to grab the current innerHTML (total) and use parseInt() to convert from a string to a number, then add your new cards that have been chosen, then assign this new value to the innerHTML of your element.
Try the following
function random_card(){
var rand = Math.floor(Math.random()*cards.length);
var html = "card: "+cards[rand][0]+"<br/>points: "+cards[rand][1]+"<br/><br/>";
document.getElementById("Player").innerHTML += html;
var punten = cards[rand][1];
var curPoints = parseInt(document.getElementById("Points").innerHTML, 10) || 0;
var total = curPoints + parseInt(punten, 10);
document.getElementById("Points").innerHTML = total;
}
More info on parseInt() here
EDIT
I've added this line -
var curPoints = parseInt(document.getElementById("Points").innerHTML, 10) || 0;
Which will try and convert the innerHTML of the "Points" div, but if it is empty (an empty string converts to false) then curPoints will be equal to 0. This should fix the issue of the div being blank at the start.
innerHTML is a string and JavaScript uses + for both string concatenation as numeric addition.
var pointsInHtml = parseInt(document.getElementById("Points").innerHTML, 10);
pointsInHtml += punten;
document.getElementById("Points").innerHTML = punten;
The second parameter 10 of the parseInt method is usually a good idea to keep there to avoid the function to parse it as an octal.
It might be easier to keep a points variable and only at the end put it in the #Points container, that would make the parseInt no longer necessary
innerHTML will be a string, so you need to convert it into an integer prior to adding the card value :)
function random_card(){
var rand = Math.floor(Math.random()*cards.length);
var html = "card: "+cards[rand][0]+"<br/>points: "+cards[rand][1]+"<br/><br/>";
document.getElementById("Player").innerHTML += html;
var punten = cards[rand][1],
curPunten = parseInt(document.getElementById('Points').innerHTML);
document.getElementById("Points").innerHTML = curPunten + punten;
}

Passing and returning an object to / from a function in Javascript.

I've done some digging on the above topic but am now more confused than when I started.
I have a unit converter that I'm working on.
It's working fine as a base model but I'm now trying to make it more modular.
There are many units and many conversions.
My plan is to have a function that determines what type of conversion is required, temperature, area etc etc, that can then call the appropriate function to carry out the math.
I'm very new to JS which isn't helping matters as it could be a simple mistake that I'm making but it's just as likely that I'm getting huge errors.
I think the problem is passing the object to the next function and then using it.
I've played with the code a great deal and tried many different suggestions online but still no success.
here is my code:
<script type="text/javascript">
function Convert(from, to, units, res){
this.from = from;
this.to = to;
this.units = units;
this.res = res;
}
Convert.convertUnits = function(){
var measurementType = $(".from option:selected").attr("class");
var result = "invalid input";
var input = parseInt(this.units.val());
if(measurementType == "temp"){
var test = new Convert($("#from"), $("#to"), $("#units"), $("#result"));
test.convertTemp();
console.log('Did we get this far?!?! ::', measurementType);
}
console.log('or not???? ::', measurementType);
}
Convert.prototype.convertTemp = function(){
var result = "invalid input";
var input = parseInt(this.units.val());
var f = this.from.val();
var t = this.to.val()
if(!isNaN(input)) {
if(f == "degC"){
if(t == "degF"){
result = input * 1.8 + 32;
}
if(t == "kelvin"){
result = input + 273.15;
}
}
}
console.log('Parsed input is', input, "and result is", result);
this.res.val(result);
return result;
}
//var calcTempTest = new Convert($("#from"), $("#to"), $("#units"), $("#result"));
//var test = new Convert($("#from"), $("#to"), $("#units"), $("#result"));
$("#btnConvert").click.convertUnits();
</script>
The first obvious problem is this line:
$("#btnConvert").click.convertUnits();
This tries to call a convertUnits() method defined on the click method of the jQuery object returned by $("#btnConvert"). There is no such method, so you get'll get an error about how click has no method 'convertUnits'.
What you want to be doing there is binding the convertUnits() function as a click handler, which you do by passing it to the .click() method as an argument:
$("#btnConvert").click(Convert.convertUnits)
It doesn't make sense to have declared convertUnits() as a property of Convert(), though, so (although it will work as is) I'd change it to just be:
function convertUnits() {
// your code here
}
$("#btnConvert").click(convertUnits);
The only other thing stopping the code working is that on this line:
var input = parseInt(this.units.val());
...you use this assuming it will be a Convert object with a units property but you haven't yet created a Convert object - you do that inside the if(measurementType == "temp") block with this line:
var test = new Convert($("#from"), $("#to"), $("#units"), $("#result"));
So move that line to the beginning of the function and then use test instead of this:
function convertUnits(){
var test = new Convert($("#from"), $("#to"), $("#units"), $("#result"));
var measurementType = $(".from option:selected").attr("class");
var result = "invalid input";
var input = parseInt(test.units.val());
if(measurementType == "temp"){
test.convertTemp();
console.log('Did we get this far?!?! ::', measurementType);
}
console.log('or not???? ::', measurementType);
}
Working demo: http://jsfiddle.net/jT2ke/
Some unrelated advice: parseInt() doesn't really make sense for a number to feed into your converter, because the user might want to enter decimal values. You can use parseFloat() instead, or the unary plus operator:
var input = +test.units.val();
But if you want parseInt() it is generally recommended to pass it a second argument to specify the radix:
var input = parseInt(test.units.val(), 10);
...because otherwise if the input text has a leading zero some browsers will assume the value is octal rather than base ten. (parseFloat() and the unary plus don't have that issue.)
I think you should not implement the method convertUnits inside Convert object. And the new code will look like the following:
convertUnits = function(){
var measurementType = $(".from option:selected").attr("class");
var result = "invalid input";
if(measurementType == "temp"){
var test = new Convert($("#from"), $("#to"), $("#units"), $("#result"));
test.convertTemp();
console.log('Did we get this far?!?! ::', measurementType);
}
console.log('or not???? ::', measurementType);
}
Now you can initiate the convertUnits on the button click:
$("#btnConvert").click(function(){new convertUnits()});

Jquery/Javascript - Can't increment number (tweet-id)

I don't get it.
I can't increment the Tweet-ID ...
Here is a demo: http://jsbin.com/idupoq/1/edit
glb = {};
glb.lastTweetId = 0;
getTweets();
function getTweets()
{
console.info('# LAST ID');
console.log(glb.lastTweetId);
console.info('# TEST 1');
glb.lastTweetId++;
console.log(glb.lastTweetId);
console.info('# TEST 2');
glb.lastTweetId = glb.lastTweetId+1;
console.log(glb.lastTweetId);
console.info('# TEST 3, OK IS INT BUT PARSE AGAIN ');
glb.lastTweetId = parseInt(glb.lastTweetId);
glb.lastTweetId++;
console.log(glb.lastTweetId);
$.getJSON('http://search.twitter.com/search.json?q=%23wwm&since_id='+glb.lastTweetId+'&include_entities=true&result_type=mixed&lang=de&callback=?', function(data, textStatus)
{
if(data.results.length > 0)
{
glb.lastTweetId = data.results[0]['id'];
}
glb.tm= setTimeout('getTweets();',5000);
});
}
Thanks in advance!
This happens because the received ID is out of range of Number format, e.g.
271567725082578940 + 1 = 271567725082578940
You should use special libraries to work with large numbers. Some examples:
https://github.com/jtobey/javascript-bignum
http://jsfromhell.com/classes/bignumber
As others have said already, it is because of Number cannot express 271567725082578941. If all you ever want to do to this number is to increase it by one, then the following function should be all you need:
function stringInc(v){
var digits = v.toString().split('');
var i = digits.length-1;
while (digits[i]==9 && i>0){
digits[i] = 0;
i--;
}
digits[i] = 1+parseInt(digits[i]);
return digits.join('');
}
If you expect to want to do something more with the number, then you might be better off using a BigNumber library as suggested by VisioN.
Either way, you should note that you cannot read the tweet id from data.results[0]['id'], because that is interpreted as a Number and rounded to 271567725082578940. You need to use data.results[0]['id_str'].
See updated jsbin here: http://jsbin.com/idupoq/19/. Notice the console is logging the result from the server:
...
"geo":null,
"id": 271580395022217200,
"id_str":"271580395022217216",
"iso_language_code":"de"
...
So the value 271567725082578940 that you have been observing is incorrect as well.
Dirty but short
http://jsbin.com/idupoq/18/edit
glb.lastTweetId = ''+data.results[0]['id']+'';
var lastTwoDig = parseInt(glb.lastTweetId.substr(glb.lastTweetId.length-2));
var startDigit = glb.lastTweetId.substring(0, glb.lastTweetId.length-2);
lastTwoDig++;
if(lastTwoDig==01){ lastTwoDig = '01'; }
console.log(glb.lastTweetId);
console.log(' '+startDigit+''+lastTwoDig+' ');

What is the most optimized or simplest way to reduce a file name in javascript

I recently created a function in javascript that takes in a file name and a max character limit where the result needs to follow these rules:
Always include file extension
If shrinking occurs, leave the first part and last part of the file name intact.
Always replace the removed characters with '...'
If file length is under the max then do nothing
You can assume the max is a least 5 chars long
Now I've already solved this, but it got me thinking if there is a more elegant or simple way to do this in javascript using regular expressions or some other technique. It also gave me an opportunity to try out jsFiddle. So with that in mind here is my function:
function ReduceFileName(name, max){
if(name.length > max){
var end = name.substring(name.lastIndexOf('.'));
var begin = name.substring(0, name.lastIndexOf('.'));
max = max - end.length - 3;
begin = begin.substr(0,max/2) + '...' + begin.substr(begin.length-(max/2) , max/2 + 1);
return begin + end;
}
return name;
}
And here it is on js Fiddle with tests
I'm not sure that regular expressions will be necessarily more elegant, but so far I came up with the following which passes your tests:
function ReduceFileName(name, max){
if(name.length > max) {
var ell ="\u2026"; // defines replacement characters
var ext = (/\.[^\.]*$/.exec(name) || [""])[0]; // gets extension (with dot) or "" if no dot
var m = (max-ell.length-ext.length)/2; // splits the remaining # of characters
var a = Math.ceil(m);
var z = Math.floor(m);
var regex = new RegExp("^(.{"+a+"}).*(.{"+z+"})"+ext, "");
var ret = regex.exec(name);
return ret[1]+ell+ret[2]+ext;
}
return name;
}
Since I didn't get much activity on this, I'm assuming there isn't a much better way to do this, so I'll consider my method as the answer until someone else comes up with something else.
function ReduceFileName(name, max){
if(name.length > max){
var end = name.substring(name.lastIndexOf('.'));
var begin = name.substring(0, name.lastIndexOf('.'));
max = max - end.length - 3;
begin = begin.substr(0,max/2) + '...' + begin.substr(begin.length-(max/2) , max/2 + 1);
return begin + end;
}
return name;
}

Categories