this is a simple puzzle game using html 5 drag and drop to move spans to their correct spot. A timer starts on the first drag (function drag(e)), and stops when there are no more spans left in the reserve (not shown).
(side question : is there a standard way of beautifying the (m)m : ss timer output I want, or do I have to go on as I am?)
Why does the timer() function work perfectly in chrome and firefox, and yet the seconds reset in Opera at 8 seconds? If I don't try to beautify the seconds and use the commented out line instead, it works perfectly.
Best regards!
var timerOn = false;
function drag(e) {
if (timerOn == false) {
timerOn = window.setInterval(function(){ timer() }, 1000);
}
...
}
function timer() {
var content = document.getElementById("timer").textContent.split(":");
if (parseInt(content[1]) == 59) {
content[0] = (parseInt(content[0]) + 1).toString();
content[1] = "00";
}
else {
var s = parseInt(content[1]) + 1;
content[1] = (s < 10 ? "0" : "") + s.toString();
//~ content[1] = s.toString();
}
document.getElementById("timer").textContent = content[0] + ":" + content[1];
}
....
<span id="timer">0:00</span>
Because some browsers extend JavaScript's parseInt to treat the prefix 0 to mean "octal", and 08 is an invalid octal number.
In the various places you use parseInt, give it its second argument (the radix — e.g., number base — to use), e.g. parseInt(str, 10). (This is a good idea generally, for this very reason.)
I'm surprised that you're still finding this behavior in an up-to-date browser, though, as the ECMAScript5 specification released three and a half years ago explicitly forbids extending parseInt in that way, as noted in Annex E - Additions and Changes in the 5th Edition that Introduce Incompatibilities with the 3rd Edition:
15.1.2.2: The specification of the function parseInt no longer allows implementations to treat Strings beginning with a 0 character as octal values.
...and I don't think §B.1.1 - Additional Syntax - Numeric Literals applies to parseInt.
Related
I’m working on a Calculator app. I'm collecting the numbers (1,2,3…) and operators (+,-,*,/, =) as parameters from html onclick method and using eval() in JavaScript to compute the expression. Something like this..
<button type="submit" class="button-style" onclick="myfunc('7')" id="seven"> 7 </button>
<button type="submit" class="button-style" onclick="myfunc('8')" id="eight"> 8 </button>
<button type="submit" class="button-style" onclick="myfunc('9')" id="nine"> 9 </button>
My JavaScript file:-
function myfunc(para) {
if (para != "=" && para != "AC" && para != "ERS" && para != "+/-") {
document.getElementById("user-input").innerHTML += para;
document.getElementById("user-input").style.display = "block";
} else if (para === "ERS") {
document.getElementById("user-input").innerHTML = document
.getElementById("user-input")
.innerHTML.slice(0, -1);
} else if (para === "AC") {
document.getElementById("user-input").innerHTML = " ";
document.getElementById("result").innerHTML = " ";
} else if (para === "+/-") {
let newvar = document.getElementById("user-input").innerHTML;
let anothervar = newvar * -1;
document.getElementById("user-input").innerHTML = anothervar;
} else {
let evaldata = document.getElementById("user-input").innerHTML;
let newevaldata = evaldata.replace(/^0+/, ""); //Omitting the leading zeroes
let resultData = eval(newevaldata);
let num = resultData.toFixed(2);
document.getElementById("result").innerHTML = num;
}
}
And this is how the calculator looks like:-
Calculator
My question:
Everything is working fine except one strange issue.
Whenever I try to add 010+100 it shows the result as 108
or if I try 0000010+1000 the result is 1008,
or 0023+3200 gives 3219.
How is that possible and how to omit the preceding zeroes, I have tried with a regular expression but it doesn't work. Without having preceding zeros the app works just fine. I am stuck here. Please help me out. Thanks.
Here is a screenshot:-
calculation error
Remove leading zeroes from numbers in a mathematical operation using this helper function:
const cleanNum = (str) => str.replace(/\d*(\.\d+)?/g, n => n && +n);
Example:
const cleanNum = (str) => str.replace(/\d*(\.\d+)?/g, n => n && +n);
const userStr = "1e2 - 0011 + 0.1100 * 00.0001 -0+ -0 / (001 + 010 + 000) - 00100";
const clean = cleanNum(userStr); // 1e2 - 11 + 0.11 * 0.0001 -0+ -0 / (1 + 10 + 0) - 100
console.log(clean);
const result = new Function(`return (${clean})`)();
console.log(result); // -10.999989
And now some additional info:
Never use eval. It's considered evil specially from a user-input — since it evaluates inside the browser any user-submitted value.
Use new Function instead.
Although (as mistakenly suggested) being an issue of parseInt() — it's not.
parseInt("010") will return the correctly expected 1010 (base 10) in all modern browsers. There's a deprecation of 0-prefixed octal literals in strict-mode which can be seen at work by specifically using parseInt, as seen.
The issue is strictly with the 010 Octal representation due to the leading 0 — in "sloppy mode"
console.log(010) // 8
You could adhere to a more battle-proof syntax by using Strict Mode
"use strict"
console.log(010) // Uncaught SyntaxError: Octal literals are not allowed in strict mode
eval("010 + 100") evaluates the first operand 010 as octal (value 8 in decimal), resulting in 8 + 100. Just like doing i.e: parseInt("011", 8) === 9; // true by using intentionally the radix 8.
Even if suggested to use new Function - the issue still remains true
"use strict";
console.log(new Function("return (010 + 100);")()) // 108
therefore: it's your job as a developer to:
get rid of any leading "0" from your String before passing them to new Function.
Finally, back to your specific code:
Use new Function instead of evil (OK, you got that one by now)
<button type="submit" should be <button type="button"
Don't use HTML-inline on* handlers like onclick="myfunc('1')". JS should be in one place only, and that's the respective <script> tag or file. Use Element.addEventListener() instead.
Don't use document.getElementById("user-input").innerHTML from a contenteditable element. Contenteditable is a total browser mess and we're still waiting for a better API to emerge - and it's an unreliable. Use document.getElementById("user-input").value instead, from an <input> Element.
Cache your elements you're planning to reuse beforehand. Querying the DOM on every button click might result in a poor user experience (and a laggy calculator).
Don't use IDs in HTML, JS. Imagine you want many calculators (as components) in a single page; by using IDs in multiple places could raise unwanted bugs singe ID is supposed to be unique.
So I have this script:
function makeActive() {
var element, name, arr;
element = document.getElementById("liveChat");
name = "active";
arr = element.className.split(" ");
if (arr.indexOf(name) == -1) {
element.className += " " + name;
}
}
var currentTime = new Date();
var currentTimeFormatted = currentTime.toLocaleTimeString();
if(currentTimeFormatted >= '08:00:00' && currentTimeFormatted <= '16:30:00'){
makeActive();
}
Which works perfectly in Chrome, however in IE the class doesn't get added.
If I remove the
&& currentTimeFormatted <= '16:30:00'
IE also adds the class. Why would adding a second condition, break this script within IE?
To make this a tad easier than having to use && and || mix, or if your values are stored somewhere in a static file etc. You could create a kind of pseudo time, by multiply each section.
eg.
const cTime = new Date();
const ptime =
cTime.getHours() * 10000 +
cTime.getMinutes() * 100 +
cTime.getSeconds();
if (ptime >= 80000 && ptime <= 163000) {
console.log("Active");
} else {
console.log("InActive");
}
You are doing string comparisons, which means that the browser and locale dependent output of toLocaleTimeString() screws your code in IE, and possibly also in other browsers or regions, because this function is solely intended for producing a human-readable time representation.
So you should either:
(1) Use a string representation that is standardized, e.g. invoking toISOString(). This will also get rid of time zone problems, because the result will always be in UTC time:
var currentTimeFormatted = new Date().toISOString(); // 2018-11-07T12:28:12.448Z'
currentTimeFormatted = currentTimeFormatted.substr(currentTimeFormatted.indexOf('T') + 1, 8); // 12:27:12
Now the rest of your code will work (assuming you 08:00:00 and 16:30:00 are UTC times).
(2) Extract the hour and minute parts of the new Date() and compare those to integers:
var currentTime = new Date();
if(currentTime.getHours() >= 8
&& // similarly a comparison to < 16:30
) {
makeActive();
}
(3) Use the great solution by Keith (see below), which I think is the best way to go
IE's implementation of date.toLocaleTimeString() adds non-printable characters into the string. The easiest way to deal with them is to trim them from the string;
currentTimeFormatted = currentTime.toLocaleTimeString().replace(/[^ -~]/g,'')
When dealing with localized timezones and timezone comparison, it might be worth trying a library like moment.js which can also deal with comparing values using the isBetween funciton
Edit
As the other solutions have suggested - using toLocaleTimeString() is not a safe method of performing date comparison and should be avoided.
Curious about what was the maximum string length I could get in Javascript, I tested it myself, today, on my Firefox 43.0.1, running in Windows 7. I was able to construct a string with length 2^28 - 1, but when I tried to create a string with one more char, Firebug showed me the "allocation size overflow" error, meaning the string must be less than 256 MB.
Is this the same thing for all browsers, all computers, all operational systems, or it depends?
I created the following snippet to find out the limit:
(function() {
strings = ["z"];
try {
while(true) {
strings.push(strings[strings.length - 1] + strings[strings.length - 1]);
}
} catch(err) {
var k = strings.length - 2;
while(k >= 0) {
try {
strings.push(strings[strings.length - 1] + strings[k]);
k--;
} catch(err) {}
}
console.log("The maximum string length is " + strings[strings.length - 1].length);
}
})();
If you are running a different browser/OS, I would like to see your results. My result was The maximum string length is 268435455.
P.S.: I searched around for an answer, but the most recent topic I found was from 2011, so I am looking for a more up-to-date information.
Characters are stored on 16 bits
When you see that 256*2**20 characters are in a string, that does not mean that 256 megabytes of memory is allocated. JavaScript stores every character on two bytes (as it is utf16 encoded by the specification).
A word about ropes
Todays browsers (even IE) store strings in an advanced way, most often using a rope datastructure.
Ropes do not need a coherent memory region to be allocated
Can even deduplicate substrings, that means s+s does not necessarily use twice the memory as s
Concatenation is very fast
Element access is a bit slower
By examining some runs in IE and Chrome, I would say that both of them use some lazy evaluation for strings, and will try to expand them occasionally. After running the following snippet, none of the browsers used more memory than before. But if I tried to manipulate the stored window.LONGEST_STRING in the console, IE throw an out of memory error, and chrome froze for a short time, and consumed a lot of memory (>2 GB).
ps: On my laptop IE11 had a maximum string size of 4 GB, Chrome had 512 MB
Browser behaviour
IE11
Chrome47
A faster algorithm to determine max string size
var real_console_log = console.log;
console.log = function(x) {
real_console_log.apply(console, arguments);
var d = document,b=d.body,p=d.createElement('pre');
p.style.margin = "0";
p.appendChild(d.createTextNode(''+x));
b.appendChild(p);
window.scrollTo(0, b.scrollHeight);
};
function alloc(x) {
if (x < 1) return '';
var halfi = Math.floor(x/2);
var half = alloc(halfi);
return 2*halfi < x ? half + half + 'a' : half + half;
}
function test(x) {
try {
return alloc(x);
} catch (e) {
return null;
}
}
function binsearch(predicateGreaterThan, min, max) {
while (max > min) {
var mid = Math.floor((max + min) / 2);
var val = predicateGreaterThan(mid);
if (val) {
min = mid + 1;
} else {
max = mid;
}
}
return max;
}
var maxStrLen = binsearch(test, 10, Math.pow(2, 52)) - 1;
console.log('Max string length is:');
console.log(maxStrLen + ' characters');
console.log(2*maxStrLen + ' bytes');
console.log(2*maxStrLen/1024/1024 + ' megabytes');
console.log('');
console.log('Store longest string');
window.LONGEST_STRING = alloc(maxStrLen);
console.log('Try to read first char');
console.log(window.LONGEST_STRING.charAt(0));
console.log('Try to read last char');
console.log(window.LONGEST_STRING.charAt(maxStrLen - 1));
console.log('Try to read length');
console.log(window.LONGEST_STRING.length);
A bug report for the chromium tracker has this comment:
... When allocation fails, we create a
Failure pointer encoding the amount requested, as well as some tag and
type bits. This puts a limit on the maximally possible allocation
request in 32-bit versions of 2^27-1. The maximal flat string length is
~2^28 (512MB space), and the maximal string length is 2^29-1...
Note that this is from 2009, so I imagine that this still has consequences in current versions of V8 as the previous link is in regard to a NodeJS tool running into limits of toString().
Internal implementations can use either UCS2 or UTF16. As #hege_hegedus suggested, atleast Firefox uses the Rope structure (https://dxr.mozilla.org/mozilla-central/search?q=%2Btype-ref%3ARopeBuilder). The codes give me below results:
CHROME VERSION
39.0.2171.95
OS VERSION
Linux: 3.13.0-43-generic
Firefox 34.0
Chrome output (from ##hege_hegedus code):
Max string length is:
268435440 characters
536870880 bytes
511.9999694824219 megabytes
Store longest string
Try to read first char
a
Try to read last char
a
Try to read length
268435440
Firefox output (from OP code):
"The maximum string length is 268435455"
Archived at http://gpupowered.org/string_js.txt
I have the following code in an external javascript file. I am getting an error on this line below: guessNum = inGuess.parseInt();
firebug tells me the parseInt is not a function. I thought all things in js were basically objects (at least that is what I remember reading in W3School). I am sure this is something simple, I am just stuck. Any suggestions are welcome. Thanks
function inputNum()
{
/* initialize variables */
var inGuess = "";
var loopCt;
var guessResult = "";
var correctNum = 26;
var guessNum = 0;
for (loopCt=1;loopCt<11;loopCt++)
{
inGuess = prompt("Please enter your guess(enter -1 to exit) Do not press Enter","0");
if (inGuess == "-1") { break; }
if (inGuess==null || inGuess=="")
{
alert("Blanks are not allowed. To exit enter '-1'.");
}
else
{
guessNum = inGuess.parseInt();
if (inGuess == "26")
{
alert("Congratulations, you guess correctly!");
guessResult="Correct!";
}
else
if (guessNum < correctNum)
{
guessResult="Too low";
}
else
{
guessResult="Too high";
}
document.getElementById('emp'+loopCt).innerHTML=inGuess;
document.getElementById('ct'+loopCt).innerHTML=guessResult;
}
}
}
parseInt is a global function. You are trying to access it off of a string object, where it doesn't exist.
guessNum = parseInt(inGuess, 10); // Tell it what base to use. Protect against 08 being interpretued as octal.
That would be the correct way to handle this.
parseInt Mozilla Developer Network Docs
Footnote - parseInt can return NaN which when compared with typeof actually returns number
parseInt is a method on window, not on a string. You want
guessNum = parseInt(inGuess, 10);
The second argument insures that your code will treat the first argument as a base-10 number, meaning it will correctly parse "010" as 10 and reject "0x10" instead of parsing it as 16.
I thought all things in js were basically objects
They are objects, but that doesn't mean that all objects have the same set of methods defined on them.
If you do want to use it like that for whatever exotic reason, you can define prototype on the String object:
String.prototype.parseInt = function() {
return parseInt(this,10);
}
var inGuess = "26";
alert(inGuess.parseInt());
Your syntax isn't quite right... From the console:
> x = parseInt("2", 10)
2
Also, something to keep in mind, which come from the docs...
If the input string begins with "0", radix is eight (octal) or 10 (decimal). Exactly which radix is chosen is implementation-dependent. ECMAScript 5 specifies that 10 (decimal) is used, but not all browsers support this yet. For this reason always specify a radix when using parseInt.
parseInt() Documentation
inGuess is a string and string does not have parseInt function. parseInt is a global function.
do this:
guessNum = parseInt(inGuess);
While playing around with random numbers in JavaScript I discovered a surprising bug, presumably in the V8 JavaScript engine in Google Chrome. Consider:
// Generate a random number [1,5].
var rand5 = function() {
return parseInt(Math.random() * 5) + 1;
};
// Return a sample distribution over MAX times.
var testRand5 = function(dist, max) {
if (!dist) { dist = {}; }
if (!max) { max = 5000000; }
for (var i=0; i<max; i++) {
var r = rand5();
dist[r] = (dist[r] || 0) + 1;
}
return dist;
};
Now when I run testRand5() I get the following results (of course, differing slightly with each run, you might need to set "max" to a higher value to reveal the bug):
var d = testRand5();
d = {
1: 1002797,
2: 998803,
3: 999541,
4: 1000851,
5: 998007,
10: 1 // XXX: Math.random() returned 4.5?!
}
Interestingly, I see comparable results in node.js, leading me to believe it's not specific to Chrome. Sometimes there are different or multiple mystery values (7, 9, etc).
Can anyone explain why I might be getting the results I see? I'm guessing it has something to do with using parseInt (instead of Math.floor()) but I'm still not sure why it could happen.
The edge case occurs when you happen to generate a very small number, expressed with an exponent, like this for example 9.546056389808655e-8.
Combined with parseInt, which interprets the argument as a string, hell breaks loose. And as suggested before me, it can be solved using Math.floor.
Try it yourself with this piece of code:
var test = 9.546056389808655e-8;
console.log(test); // prints 9.546056389808655e-8
console.log(parseInt(test)); // prints 9 - oh noes!
console.log(Math.floor(test)) // prints 0 - this is better
Of course, it's a parseInt() gotcha. It converts its argument to a string first, and that can force scientific notation which will cause parseInt to do something like this:
var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
Silly me...
I would suggest changing your random number function to this:
var rand5 = function() {
return(Math.floor(Math.random() * 5) + 1);
};
This will reliably generate an integer value between 1 and 5 inclusive.
You can see your test function in action here: http://jsfiddle.net/jfriend00/FCzjF/.
In this case, parseInt isn't the best choice because it's going to convert your float to a string which can be a number of different formats (including scientific notation) and then try to parse an integer out of it. Much better to just operate on the float directly with Math.floor().