Functions giving unexpected behavior in JavaScript - javascript

I am writing a program to generate primes in JavaScript. The problem is that the behavior of the program is very strange, it seems that the functions do not execute in order which prevents it from executing properly. Here is my code:
<!DOCTYPE html>
<html>
<head>
<title>Generate Some Primes!</title>
</head>
<body>
<h1>Let's Generate Some Primes!!!</h1>
<form id="userInput">
<ul>
<li>
<label>Lower Bound (inclusive):</label>
<input type="number" id="lower">
</li>
<li>
<label>Upper Bound (exclusive):</label>
<input type="number" id="upper">
</li>
<li>
<input type="submit" value="Generate!">
</li>
</ul>
</form>
<p id="messageBox"></p>
<ul id="primes"></ul>
<script>
var primesList = document.getElementById("primes");
var lower;
var upper;
var NOT_STARTED = "Generating ...";
var DONE = "Done!";
window.onload = function() {
userInput.onsubmit = function(e) {
e.preventDefault();
writeMessage(NOT_STARTED);
if (checkInput()) {
checkNumbers();
writeMessage(DONE);
}
}
}
function checkInput() {
initializeBounds();
if (lower >= upper) {
writeMessage("Lower bound must be less than upper bound.");
return false;
}
return true;
}
function initializeBounds() {
lower = Math.round(document.getElementById("lower").value);
upper = Math.round(document.getElementById("upper").value);
primesList.innerHTML = "";
}
function checkNumbers() {
for (var i = lower; i < upper; i++) {
if (isPrime(i))
writePrime(i);
}
}
function writePrime(p) {
primesList.innerHTML += "<li>" + p + "</li>";
}
function writeMessage(message) {
var messageBox = document.getElementById("messageBox");
messageBox.innerHTML = message;
}
function isPrime(p) {
if (p < 2)
return false;
for (var b = p - 1; b > 1; b--)
if (GCD(p, b) > 1)
return false;
return true;
}
function GCD(a, b) {
if (b == 0)
return a;
else
return GCD(b, a % b);
}
</script>
</body>
</html>
These are my problems:
When the lower bound is close to the upper bound, and they are both relatively large numbers, no output is produced (for example try inputting 900 as the lower bound and 1000 as the upper bound), even though I know for sure prime numbers exist there, and that my method for generating them works.
The primes should be displayed on the screen as they are generated, and not at the end when they have all been created. If you input 0 as the lower bound and 10000 as the upper bound you will clearly see this issue (I suggest running the snippet full screen and watching the scrollbar size).
This is related to number 2. The Generating ... message is never shown, only Done.
EDIT: I made some changes to the code that solved the first issue.

I would suggest using Promises to break the blocking behaviour of the for loop and give the browser/UI enough time to render the results.
<body>
<h1>Let's Generate Some Primes!!!</h1>
<form id="userInput">
<ul>
<li>
<label>Lower Bound (inclusive):</label>
<input type="number" id="lower">
</li>
<li>
<label>Upper Bound (exclusive):</label>
<input type="number" id="upper">
</li>
<li>
<input type="submit" value="Generate!">
</li>
</ul>
</form>
<p id="messageBox"></p>
<ul id="primes"></ul>
<script>
var primesList = document.getElementById("primes");
var lower;
var upper;
var NOT_STARTED = "Generating ...";
var DONE = "Done!";
window.onload = function() {
userInput.onsubmit = function(e) {
e.preventDefault();
writeMessage(NOT_STARTED);
if (checkInput()) {
checkNumbers()
.then(function() {
writeMessage(DONE);
})
}
}
}
function checkInput() {
initializeBounds();
if (lower >= upper) {
writeMessage("Lower bound must be less than upper bound.");
return false;
}
return true;
}
function initializeBounds() {
lower = Math.round(document.getElementById("lower").value);
upper = Math.round(document.getElementById("upper").value);
primesList.innerHTML = "";
}
function checkNumbers() {
var promise = Promise.resolve();
for (var i = lower; i < upper; i++) {
(function(i) {
promise = promise.then(function() {
if (isPrime(i))
return writePrime(i);
});
})(i);
}
return promise;
}
function writePrime(p) {
return new Promise(function(resolve, reject) {
primesList.innerHTML += "<li>" + p + "</li>";
writeMessage("Generating ..." + Math.round(100 * p / (upper - lower)) + "%");
setTimeout(function() {
resolve();
}, 0);
});
}
function writeMessage(message) {
var messageBox = document.getElementById("messageBox");
messageBox.innerHTML = message;
}
function isPrime(p) {
if (p < 2)
return false;
for (var b = p - 1; b > 1; b--)
if (GCD(p, b) > 1)
return false;
return true;
}
function GCD(a, b) {
if (b == 0)
return a;
else
return GCD(b, a % b);
}
</script>
</body>
External link to jsFiddle.

1: Use parseInt() to correctly convert the input (string) to a number.
2, 3: The for loop blocks the window thread, so this is why you aren't seeing the step-by-step results; the DOM will be refreshed only when the code finishes. The message is actually being set, but you can't see it because the process is blocked.
I have edited on your snippet, using the setTimeout() function, which will wait some time before calls.
<!DOCTYPE html>
<html>
<head>
<title>Generate Some Primes!</title>
</head>
<body>
<h1>Let's Generate Some Primes!!!</h1>
<form id="userInput">
<ul>
<li>
<label>Lower Bound (inclusive):</label>
<input type="number" id="lower">
</li>
<li>
<label>Upper Bound (exclusive):</label>
<input type="number" id="upper">
</li>
<li>
<input type="submit" value="Generate!">
</li>
</ul>
</form>
<p id="progress"></p>
<ul id="primes"></ul>
<script>
var primesList = document.getElementById("primes");
var lower;
var upper;
var NOT_STARTED = false;
var DONE = true;
window.onload = function() {
userInput.onsubmit = function(e) {
e.preventDefault();
/* 3: let's give some time for message */
writeProgressMessage(NOT_STARTED);
initializeBounds();
window.setTimeout(function () {
checkNumbers(lower, upper);
}, 1000); // waiting for 1s to start
}
}
function initializeBounds() {
/* correctly convert the input's for numbers, using parseInt */
lower = parseInt(document.getElementById("lower").value, 10);
upper = parseInt(document.getElementById("upper").value, 10);
primesList.innerHTML = "";
}
function checkNumbers(current, upper) {
if (isPrime(current))
writePrime(current);
/* not yet finished */
if (current != upper) {
setTimeout(function () {
checkNumbers(current+1, upper);
}, 100); // wait 100ms for the next check
}
}
function writePrime(p) {
primesList.innerHTML += "<li>" + p + "</li>";
}
function writeProgressMessage(done) {
var progress = document.getElementById("progress");
if (!done)
progress.innerHTML = "Generating ...";
else
progress.innerHTML = "Done!";
}
function isPrime(p) {
if (p < 2 || !Number.isInteger(p))
return false;
for (var b = p - 1; b > 1; b--)
if (GCD(p, b) > 1)
return false;
return true;
}
function GCD(a, b) {
if (b == 0)
return a;
else
return GCD(b, a % b);
}
</script>
</body>
</html>

Related

Uncaught ReferenceError: main is not defined

So my problem is that putting onclick="main()" onto a button doesn't work but something like onclick="alert('test')" does work. When I look in the console, it throws Uncaught ReferenceError: main is not defined when I press the button.
I have tried putting all the functions that aren't main() inside of main() but that didn't work. I've also tried putting semicolons after all the functions thinking that I just made up the fact that you don't have to put a semicolon after a function.
I'm confused because I copy/pasted another JavaScript program of mine and edited it to make this and that one works fine.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<title>The Worst Sorting Algorithm Ever Made</title>
<style>
/*----- START OF STYLESHEET -----*/
*, :before, :after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/*body {
margin : 0;
*/}
/*-----END OF STYLESHEET-----*/
</style>
<script>
//----- START OF SCRIPT -----//
// I can't believe I have to implement this myself
function randfloat(min, max) {
return Math.random() * (max - min) + min;
}
function randint(min, max) {
return Math.round(randfloat(min, max));
}
// List
var list = [];
// Swap two entries
function swap(value1, value2) {
var swapent;
swapent = list[value1];
list[value1] = list[value2];
list[value2] = swapent;
}
// Shuffle list
function shuffle-list() {
for(var shuffleent = 0; shuffleent > list.length; shuffleent = shuffleent + 1) {
swap(shuffleent, randint(0, list.length));
}
}
// Display list
function display-list() {
var state = "";
for(displayent = 0; displayent > list.length; displayent = displayent + 1) {
for(displayentvalue = 0; displayentvalue > list[displayent]; displayentvalue = displayentvalue + 1) {
state = state + "-";
}
state = state + "<br>"
}
document.getElementById("output").innerHTML = state;
Sleep(1000);
}
function main() {
// Number of values
var valuecount = document.getElementById("values").value;
// Self-explanatory
var isSorted = false;
alert("working");
// Generate presorted list
list = [0, 1, 2, 3];
display-list();
// Shuffle sorted list
// Sort
//while(isSorted === false) {
//}
}
//-----END OF SCRIPT-----//
</script>
</head>
<body>
<h1>The Worst Sorting Algorithm Ever Made</h1>
<form>
<label for="values">Number of equally spaced values:</label><br>
<input type="number" id="values" name="values"><br><br><br>
<input type="button" id="start" value="Start" onclick="main()"><br><br><br>
</form>
<p id="output"></p>
</body>
</html>
some error in you code:
"}"
'-' in the function name.
after fixed the error, the code worked
You should not use a - while naming your function but instead you can use these.
An underscore and replace the -.
display-list() to display_list()
Use CamelCase.
display-list() to displayList().
Function names can contain letters, digits, underscores, and dollar signs but not dash
sign
There are so many mistakes in your code.
There is no Sleep function in the JS.
You can't have - or whitespaces in the function names.
You are never going to enter in this loop for(displayent = 0; displayent > list.length; displayent = displayent + 1) you had to use < instead >
Where are you using valuecount and isSorted
In the CSS } which shouldn't be there
I edited some of your code to make it work.
// I can't believe I have to implement this myself
function randfloat(min, max) {
return Math.random() * (max - min) + min;
}
function randint(min, max) {
return Math.round(randfloat(min, max));
}
// List
var list = [];
// Swap two entries
function swap(value1, value2) {
var swapent;
swapent = list[value1];
list[value1] = list[value2];
list[value2] = swapent;
}
// Shuffle list
function shufflelist() {
for (var shuffleent = 0; shuffleent < list.length; shuffleent = shuffleent + 1) {
swap(shuffleent, randint(0, list.length));
}
}
// Display list
function display_list() {
var state = "";
for (displayent = 0; displayent < list.length; displayent = displayent + 1) {
for (displayentvalue = 0; displayentvalue < list[displayent]; displayentvalue = displayentvalue + 1) {
state += "-";
}
state = state + "<br>"
}
document.getElementById("output").innerHTML = state;
}
function main() {
// Number of values
var valuecount = document.getElementById("values").value;
// Self-explanatory
var isSorted = false;
alert("working");
// Generate presorted list
list = [0, 1, 2, 3];
display_list();
}
*,
:before,
:after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html>
<head>
<title>The Worst Sorting Algorithm Ever Made</title>
</head>
<body>
<h1>The Worst Sorting Algorithm Ever Made</h1>
<form>
<label for="values">Number of equally spaced values:</label><br>
<input type="number" id="values" name="values"><br><br><br>
<input type="button" id="start" value="Start" onclick="main()"><br><br><br>
</form>
<p id="output"></p>
</body>
</html>
Re Wrote the logic of your code
// I can't believe I have to implement this myself
function randfloat(min, max) {
return Math.random() * (max - min) + min;
}
function randint(min, max) {
return Math.round(randfloat(min, max));
}
// List
var list = [];
// Swap two entries
function swap(value1, value2) {
list[value2] = [list[value1], list[value1] = list[value2]][0];
}
// Shuffle list
function shufflelist() {
list.forEach((element, index) => {
swap(index, randint(0, list.length))
})
}
// Display list
function display_list() {
var state = "";
list.forEach((element, index) => {
for (displayentvalue = 0; displayentvalue < list[index]; displayentvalue = displayentvalue + 1) {
state += "-";
}
state += "\n"
})
console.log(state);
}
function main() {
// Number of values
var valuecount = document.getElementById("values").value;
// Self-explanatory
var isSorted = false;
alert("working");
// Generate presorted list
list = [0, 1, 2, 3];
display_list();
shufflelist();
display_list();
}
*,
:before,
:after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html>
<head>
<title>The Worst Sorting Algorithm Ever Made</title>
</head>
<body>
<h1>The Worst Sorting Algorithm Ever Made</h1>
<form>
<label for="values">Number of equally spaced values:</label><br>
<input type="number" id="values" name="values"><br><br><br>
<input type="button" id="start" value="Start" onclick="main()"><br><br><br>
</form>
<p id="output"></p>
</body>
</html>

How to fix access DOM in a loop using prompt

In a prompt message I request a number, I want to update the DOM element (input) but only work when do - while ends, I want to update the DOM after every prompt message.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Odds and Evens Game</title>
</head>
<body>
<form>
Number: <input type="text" id="number">
<input type="button" onclick="showIt()" value="ShowIt">
</form>
<script>
function showIt(){
var inputval ;
do {
inputval = prompt('Enter a number between 1 and 5 , 999 to exit');
number = parseInt(inputval);
document.getElementById('number').value= number;
}while(number !== 999);
}
</script>
</body>
</html>
sample that is running:
https://jsfiddle.net/neossoftware/73ag8whd/4/
prompt window prevents the user from accessing the rest of the program's interface until the dialog box is closed. Your while loop is calling prompt again and again blocking the access to user interface.
Use await, to paint the window before calling second prompt.
let num = 0;
let ele = document.querySelector('#num');
(async () => {
do {
await getInput()
ele.value = num;
await sleep(0);
} while(num !== 999);
})();
function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
})
}
function getInput() {
return new Promise(resolve => {
num = Number(prompt("Enter num"));
resolve(num);
});
}
Number: <input type="text" id="num">
Maybe I'm not understanding the question completely as your code does work without error except the last 999 to exit bit (on Firefox, Linux) - as that code then updates your input to 999. Why do you have this 999 to exit anyway?
Some minor changes and options below:
function showIt(){
var inputval, num;
do {
inputval = prompt('Enter a number between 1 and 5 (inclusive), 999 to exit');
num = parseInt(inputval);
if ((num != NaN) && (num >= 1) && (num <= 5) && (num != 999)) {
document.getElementById('number').value = num;
}
} while (num !== 999);
}
function updateAndExit(e) {
var inputval = prompt('Enter a number between 1 and 5 (inclusive)');
var num = parseInt(inputval);
if ((num != NaN) && (num >= 1) && (num <= 5)) {
this.textContent = num;
}
}
window.onload = function() {
var d = document.getElementById("click-to-udpate");
if (d) {
d.addEventListener("click",updateAndExit,false);
}
}
.fakeInput {
border:1px solid #886;
padding:6px;
background:#ddd;
}
.fakeInput:hover {
border:1px solid #688;
background:#fff;
cursor:pointer
}
Number: <input type="text" disabled="disabled" id="number">
<input type="button" onclick="showIt()" value="ShowIt">
<p>
Different option below:
</p>
<div id="click-to-udpate" class="fakeInput">Click to update</div>

Unable to make thss script to run on click. Need it to re run after input has been changed

I've tried many times but can not figure out how to get this full script to run on click it will not work. Need answer to change after the value of the input has been changed.
<input id="a" value="500" type="number">
<p id="b"></p>
<script>
var bills = [100, 250, 450, 950, 1150];
var money = mod(document.getElementById("a").value);
function mod(num){
if (num % 5 === 0){
return num;
} else {
return num + 5 - num % 5
}
}
function foo(num){
var index = bills.length - 1;
var splits = [];
while (money >= bills[0]){
if (money >= bills[index]){
money -= bills[index];
splits.push(bills[index]);
} else {
index--;
}
}
return splits;
}
document.getElementById("b").innerHTML = foo(money) </script>

Making a javascript hangman game and I can't get my function to apply to repeated letters

Everything about the script works great right now unless there's a repeated letter in the word. If so, then it will only display the first of the letters. For example, if the random word is "look" it would display like this "lo k".
Unfortunately the only other related javascript hangman question here was for a script that didn't actually have issues on repeated letters. For reference: how to deal with repeated letters in a javascript hangman game. Can anyone help me get through the repeated letter issue? Thanks!
My HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/jquery-1.11.2.min.js"></script>
<script src="js/jquery-1.11.2.js"></script>
<link rel="stylesheet" href="css/main.css">
<title>Hang a Blue Devil</title>
</head>
<body>
<div class="wrapper">
<h1 class="title">Hangman</h1>
<h2 class="attempt-title">You have this many attempts left: </h2>
<ul class="hangman-word">
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
<li class="tester"></li>
</ul>
<h3 class="hangman-letters"></h3>
<input class="text-value" type="text" maxlength="1" onchange="setGuess(this.value)">
<button class="text-button" onclick="checkGuess()"></button>
<p class="letters-guessed"></p>
</div>
</body>
<script src="js/hangman.js"></script>
</html>
My JS:
var hangmanWords = [
"the","of","and","a","to","in","is","you","that","it","he",
"was","for","on","are","as","with","his","they","I","at","be",
"this","have","from","or","one","had","by","word","but","not",
"what","all","were","we","when","your","can","said","there",
"use","an","each","which","she","do","how","their","if","will",
"up","other","about","out","many","then","them","these","so",
"some","her","would","make","like","him","into","time","has",
"look","two","more","write","go","see","number","no","way",
"could","people","my","than","first","water","been","call",
"who","oil","its","now","find","long","down","day","did","get",
"come","made","may","part"
];
// declared variables
var randomNumber = Math.floor(Math.random() * 100);
var randomWord = hangmanWords[randomNumber];
var underscoreCount = randomWord.length;
var underscoreArr = [];
var counter = randomWord.length +3;
var numberTest = 0;
var lettersGuessedArr = [];
var lettersGuessedClass = document.querySelector('.letters-guessed');
var li = document.getElementsByClassName('tester');
var textValue = document.querySelector('.text-value');
var attemptTitle = document.querySelector('.attempt-title');
var hangmanWordClass = document.querySelector('.hangman-word');
var hangmanLettersClass = document.querySelector('.hangman-letters');
// actions
attemptTitle.innerHTML = "You have this many attempts left: " + counter;
console.log(randomWord);
function setGuess(guess) {
personGuess = guess;
}
for (i=0;i<underscoreCount;i+=1) {
underscoreArr.push("_ ");
underscoreArr.join(" ");
var underscoreArrString = underscoreArr.toString();
var underscoreArrEdited = underscoreArrString.replace(/,/g," ");
hangmanLettersClass.innerHTML = underscoreArrEdited;
}
function pushGuess () {
lettersGuessedArr.push(personGuess);
var lettersGuessedArrString = lettersGuessedArr.toString();
var lettersGuessedArrEdited = lettersGuessedArrString.replace(/,/g," ");
lettersGuessedClass.innerHTML = lettersGuessedArrEdited;
}
function checkGuess() {
for (var i=0;i<randomWord.length;i+=1) {
if (personGuess === randomWord[i]) {
console.log(personGuess);
numberTest = i;
li[i].textContent = randomWord[i];
i += 20;
textValue.value= "";
} else if ((randomWord.length - 1) > i ) {
console.log("works");
} else {
pushGuess();
counter -= 1;
attemptTitle.innerHTML = "You have made this many attempts: " + counter;
textValue.value= "";
}
}
};
My bin:
http://jsbin.com/dawewiyipe/4/edit
You had a stray bit of code that didn't belong:
i += 20;
I took it out, and the problem went away (the loop was intended to check each character, the +=20 broke the process of checking each character)
function checkGuess() {
for (var i=0;i<randomWord.length;i+=1) {
if (personGuess === randomWord[i]) {
console.log(personGuess);
numberTest = i;
li[i].textContent = randomWord[i];
textValue.value= "";
} else if ((randomWord.length - 1) > i ) {
console.log("works");
} else {
pushGuess();
counter -= 1;
attemptTitle.innerHTML = "You have made this many attempts: " + counter;
textValue.value= "";
}
}
}
http://jsbin.com/noxiqefaji/1/edit

how to stop a message from a javascript timer from repeating?

I have a timer for my game, but the message will keep going on the mage, so it says it multiple times, i was wondering how you can get it to say it once.
<head>
<script type="text/javascript">
var c=10;
var t;
var timer_is_on=0;
function timedCount() {
document.getElementById('txt').value = c;
c = c - 1;
if (c == -1||c < -1){
var _message = document.createTextNode("You have mined 1 iron ore!");
document.getElementById('message').appendChild(_message);
startover();
}
}
function startover() {
c = 10;
clearTimeout(t);
timer_is_on=0;
doMining();
}
function doMining() {
if (!timer_is_on) {
timer_is_on = true;
t = setInterval(function () {
timedCount();
}, 1000);
}
}
</script>
<SPAN STYLE="float:left">
<form>
<input type="button" value="Mining" onClick="doMining()">
<input type="text" id="txt">
</form>
</SPAN>
<html>
<center>
<div id='message'></div>
Instead of setInterval use window.setTimeout.
This will trigger the function only once.
Edit: if you mean you want one message to appear and update every time, first add global counter:
var mineCount = 0;
Then change the code to this:
if (c <= -1) {
mineCount++;
var _message = "You have mined " + mineCount + " iron ore" + ((mineCount > 1) ? "s" : "") + "!";
document.getElementById('message').innerHTML = _message;
startover();
}
This will assign the contents of the element instead of adding to it each time.
Your startOver function calls doMining(). Surely, it shouldn't...?

Categories

Resources