3

I am new to javascript, having trouble with this assignment. My professor suggested the problem was due to needing to parseInt, but even after I added the parseInt, it still isn't working.

This displays fine in Firefox, and the "higher" and "lower" statements are displayed, but when I run it in Chrome or Edge, only the window asking for a number will render. I did look for help, but I cant see what I'm doing wrong. Most of the suggestions Ive seen online, don't address the code directly. Is this problem specific code related or something else?

function play() {

  let guess;
  let randNum = Math.floor(1 + Math.random() * 999);
  let guessed = false;

  while (guessed == false) {
    guess = window.prompt("Enter a number from 1 to 1000");
    parseInt(guess);
    if (guess == randNum) {
      document.writeln("<li>" + "Congratulations! You guessed the correct number!</li>");
      guessed = true;
      document.writeln("</ol>");
      document.writeln("Your guess: " + guess);
      document.writeln("Actual number: " + randNum);
    } else if (guess > randNum) {
      document.writeln("<li>" + guess + " is Too High. Try Again.</li>");
      document.writeln("</ol>");
    } else if (guess < randNum) {
      document.writeln("<li>" + guess + " is Too Low. Try Again.</li>");
      document.writeln("</ol>");
    }
  }

}

window.addEventListener("load", play, false);
showdev
  • 28,454
  • 37
  • 55
  • 73
studeecode
  • 31
  • 3
  • 4
    This is not the source of the problem, but I thought I'd mention it anyway. `parseInt` [returns](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#return_value) an integer value, so you might consider `guess = parseInt(guess);` – showdev Mar 26 '22 at 22:13
  • 1
    [Don't use `document.writeln`](https://stackoverflow.com/questions/802854/why-is-document-write-considered-a-bad-practice). Use `alert()` instead, until you learn about the DOM and asynchronous event handling (and don't need to use `prompt` any longer). – Bergi Mar 26 '22 at 22:18
  • 4
    ***"Only works in Firefox"* cannot be a Question title**. Imagine all questions here were like *"Does not work in Chrome"* - you wouldn't be able to google a thing. Please [edit]. – Roko C. Buljan Mar 26 '22 at 22:39
  • 3
    `document.writeln` is just not the way to go. You should only append LI elements to an OL element already present in the DOM. Learn about https://developer.mozilla.org/en-US/docs/Web/API/Element/append – Roko C. Buljan Mar 26 '22 at 22:43
  • 2
    Does this answer your question? [Why does alert appear before background changes?](https://stackoverflow.com/questions/40945552/why-does-alert-appear-before-background-changes) - essentially Firefox doesn't block rendering during an `alert` and other browsers do. – traktor Mar 26 '22 at 23:25
  • @RokoC.Buljan "Firefox" is a useful keyword when looking for an answer to this question. – traktor Mar 26 '22 at 23:32
  • @traktor The question **title** was changed in the meantime. I was not talking about the *Firefox* **Tag**. The question title was exactly like I stated above. If in doubt, see the [revision history](https://stackoverflow.com/posts/71632053/revisions) . – Roko C. Buljan Mar 26 '22 at 23:44

2 Answers2

2

To ease the querying or creation of the desired DOM Elements — create two reusable functions:

// DOM utility functions:

const find   = (selector, parent) => (parent || document).querySelector(selector);
const create = (tag, properties) => Object.assign(document.createElement(tag), properties);

that can be used to cache your Elements and use them later in your game logic

// Cache your DOM elements!
const elNumber  = find("#number"); 
const elCheck   = find("#check");
const elAnswers = find("#answers");

which will target and cache your three HTML elements by theri id attribute selector. As said above, instead of using prompt, use a better and less invasive UI (User interface) right into your App:

Enter a number from 1 to 10: <input id="number" type="text">
<button id="check" type="button">CHECK</button>
<ol id="answers"></ol>

Then create two let variables for the guessed state and one for the random number, so that when you start a new game you can change their values:

// Make available for multiple games!
let numRand;
let isGuessed;

then, giving your specific game, you need two more functions, one to start (and restart) the game and one for your game logic:

// Call this function to start a new game!
const start = () => {
  // Clear old answers
  // Reset old guessed state
  // Generate a new random number
};

// Call this function on button CHECK click!
const check = () => {
  // Game logic goes here!
}

// Assign listener to button:
elCheck.addEventListener("click", check);

// Start a new game!
start(); 

Demo time:

// DOM utility functions:

const find   = (selector, parent) => (parent || document).querySelector(selector);
const create = (tag, properties) => Object.assign(document.createElement(tag), properties);

// Task:

// Cache your DOM elements!
const elNumber  = find("#number"); // PS: remember, use const for constants!
const elCheck   = find("#check");
const elAnswers = find("#answers");

// Make available for multiple games!
let numRand;
let isGuessed; // Try to prefix boolean variables with "is*"


// Call this function to start a new game!
const start = () => {
  // Clear old answers:
  elAnswers.innerHTML = "";

  // Reset old guessed state
  isGuessed = false;

  // Generate a new random number 1 to 10:
  numRand = Math.floor(Math.random() * 9) + 1;
};


// Call this function on button CHECK click!
const check = () => {

  // Start a new game if needed
  if (isGuessed) start();

  // Get the user entered value
  // Use parseInt with radix 10 and Math.abs
  // to prevent negative numbers
  const numUser = Math.abs(parseInt(elNumber.value, 10)); 

  // Do nothing if invalid value entered:
  if (!numUser) return;

  // Update isGuessed state
  isGuessed = numRand === numUser;

  // Handle answer:
  const textAnswer = `
    You guessed: ${numUser}.
    The number is ${isGuessed ? "correct!" : numUser > numRand ? "too high." : "too low."}
    ${isGuessed ? "Congratulations!" : "Try again"}
  `;

  // Create a LI element with the answer text
  const elAnswer = create("li", {
    textContent: textAnswer
  });

  // Append your LI element!
  elAnswers.append(elAnswer);

  // Clear the current value from input:
  elNumber.value = "";
};


// Assign listener to button:
elCheck.addEventListener("click", check);

// Start a new game!
start();
Enter a number from 1 to 10: <input id="number" type="text">
<button id="check" type="button">CHECK</button>
<ol id="answers"></ol>

Additional learning resources:

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • 1
    If we're following best practices, let's also follow the JS naming conventions =) (`numRand`, rather than python `num_rand`, `find()` instead of `EL()`, `create()` instead of `ELnew()`, just spelling out `parent` instead of `par` since the engine doesn't care but humans do, etc) – Mike 'Pomax' Kamermans Mar 27 '22 at 02:57
  • 1
    @Mike'Pomax'Kamermans Yes I agree 100%, amazing suggestion. Adopted. – Roko C. Buljan Mar 27 '22 at 19:46
1

As I understand it, JavaScript is single-threaded, meaning that it has only one execution thread. Methods like prompt() and alert() block that thread. As long as prompt() is called in a loop, writeln content will not be rendered on the page.

One way to address this with your existing code is to use setTimeout() to delay the next call to prompt(), which allows content to be rendered in the meantime.

That being said, I recommend a more asynchronous method that does not rely on prompt() or writeln(). This answer is intended to explain the issue with your existing code; for a more robust strategy, see Roko's.

var randNum = Math.floor(1 + Math.random() * 999);

function ask() {
  let guess = parseInt(window.prompt("Enter a number from 1 to 1000"));
  if (guess == randNum) {
    document.writeln("<div>Congratulations! You guessed the correct number!</div>");
    document.writeln("<div>Your guess: " + guess + ". Actual number: " + randNum + "</div>");
  } else if (guess > randNum) {
    document.writeln("<div>" + guess + " is Too High. Try Again.</div>");
    setTimeout(ask, 50);
  } else if (guess < randNum) {
    document.writeln("<div>" + guess + " is Too Low. Try Again.</div>");
    setTimeout(ask, 50);
  }
}

window.addEventListener("load", ask, false);
showdev
  • 28,454
  • 37
  • 55
  • 73
  • 1
    But if we're giving an answer, we might as well remove all that `document.writeln` and replace it with proper JS, because _especially_ people new to JS should _never_ use document.write(ln) and should be taught what to use instead (in this case, ye olde document.createElement, .textContent, and appendChild) – Mike 'Pomax' Kamermans Mar 26 '22 at 22:56
  • Good point! I encourage you to write such an answer, including explanation of *why* to avoid certain code. Also note that `appendChild()` on its own may not solve the problem, because `prompt()` will still block the thread. – showdev Mar 26 '22 at 23:07