Introduction to programming

Further improving our guessing game with functions and events

Our game from earlier has still got a problem. We are still using a prompt() function to bring up a dialog box to ask players for guesses. This is still really bad for user experience. One of the core rules of user experience is to keep the number of pop ups down to a minimum. You all know how annoying popups can be. We should be able to do our entire app inside the web page, collecting data from the user with form elements. Our code currently looks like this:

The HTML:

<h1>Number guessing game</h1>
<p class="guesses"></p>
<p class="lastResult"></p>
<p class="lowOrHi"></p>

The JavaScript:

var randomNumber = Math.floor(Math.random() * 100) + 1;

var guesses = document.querySelector(".guesses");
var lastResult = document.querySelector(".lastResult");
var lowOrHi = document.querySelector(".lowOrHi");

for(i = 1; i <= 10 ; i++) {
  var userGuess = prompt("Enter guess " + i);
  guesses.innerHTML += userGuess + " ";

  if(userGuess === randomNumber) {
    lastResult.innerHTML = "Congratulations! You got it right!";
    lowOrHi.innerHTML = "";
    break;
  } else {
    lastResult.innerHTML = "Wrong!";
    if(userGuess < randomNumber) {
      lowOrHi.innerHTML = "Your guess is too low!";
    } else if(userGuess > randomNumber) {
      lowOrHi.innerHTML = "Your guess is too high!";
    }
  }
}

Before running through the below steps, find the template file that you previously wrote your cgame code into. If you don't have it or can't find it, then no matter - you can create a new copy of the template and copy the code into it.

  1. So first we need to provide a different way for the user to give us their guesses, given that we want to get rid of the prompt() function. The best way to collect information from users is to use <input> elements. In your HTML, add the following at the bottom of your </body> area.
  2. <div class="form">
      <label for="guessField">Enter your next guess: </label>
      <input type="text" class="guessField" id="guessField">
      <button class="guessSubmit">Enter Guess</button>
    </div>
  3. Now we need to change our JavaScript so that in each turn of the game, the user's guess is taken from the submitted form, and not a prompt() function. This is trickier than you'd think, so I'll give you some clues:
    • You will need to get rid of the loop and instead encapsulate the guess processing code in a function
    • You will need to add an event listener that listens for the form being submitted (the button being clicked) and as a result runs the function
    • You will need a variable that tracks what guess number we are on and is incremented each time the function runs
    • After 10 guesses we need to end the game with some kind of "Game over" message
    • When the number is guessed correctly, the game should end and no more guesses should be allowed

Lets handle these, one at a time.

Get rid of the loop and instead encapsulate the guess processing code in a function

To grab the form element references, enter the following variable definitions, beneath the existing ones:


var guessSubmit = document.querySelector(".guessSubmit");
// reference to the button that submits the guess
var guessField = document.querySelector(".guessField");
// reference to the text input field that the guess is typed into

Now replace the entire for loop with the following function:

function checkGuess() {
// replace the for loop with a function
  var userGuess = Number(guessField.value);
  // no more prompt. The guess is now equal to the value inside
  // the input field when the function is run.
  guesses.innerHTML += userGuess + " ";

  if(userGuess === randomNumber) {
    lastResult.innerHTML = "Congratulations! You got it right!";
    lowOrHi.innerHTML = "";
  } else {
    lastResult.innerHTML = "Wrong!";
    if(userGuess < randomNumber) {
      lowOrHi.innerHTML = "Your guess is too low!";
    } else if(userGuess > randomNumber) {
      lowOrHi.innerHTML = "Your guess is too high!";
    }

  }
}

Note the line var userGuess = Number(guessField.value);. This is what grabs the guess from the guessField value once the function is run. Note that we've also had to wrap it in a Number() function, to make sure the guess is treated like a number, not a string.

Add an event listener that listens to when the form is submitted (the button is clicked) and as a result runs the function

This is an easy step compared to all the changes we had to make before. Here we just need to add a line below the function that adds an onclick listener to the button that functions the function when the click happens.

Add this at the bottom of your JavaScript area:

guessSubmit.onclick = checkGuess;

Add a variable that tracks what guess number we are on and is incremented each time the function runs

Because we no longer have a loop, the guess number will no longer increment on its own. we need to emulate the loop functionality by first adding a variable that tracks the guess number. We then need to increase that value by one each time the checkGuess function is run.

Add this below the other variable definitions:

var guessCount = 1;
// Add a variable that tracks the guess number we are on

Now add the following lines. These need to go at the bottom of the else { ... } code block (just above the seoncd-to-last closing curly brace), as we want to update the guess count each time the player gets the answer wrong:

guessCount++;
// after each guess, increase the guess count
guessField.value = "";
// and empty the text input field ready for the next guess

The second line empties the form <input>element, ready for the player to enter the next guess.

After 10 guesses we need to end the game with some kind of "Game over" message

This is slightly trickier and requires us to add to our if ... else structure to check if the guessCount is equal to 10 or not. If so, we'll end the game with some kind of message. If not, we'll carry on with the code as it was before.

The existing if ... else structure looks like this:

if(userGuess === randomNumber) {
  lastResult.innerHTML = "Congratulations! You got it right!";
  lowOrHi.innerHTML = "";
} else {
  lastResult.innerHTML = "Wrong!";
  if(userGuess < randomNumber) {
    lowOrHi.innerHTML = "Your guess is too low!";
  } else if(userGuess > randomNumber) {
    lowOrHi.innerHTML = "Your guess is too high!";
  }

  guessCount++;
  // after each guess, increase the guess count
  guessField.value = "";
  // and empty the text input field ready for the next guess

}

You need to wrap all of this in the following additional if ... else structure:

if(guessCount === 10) {
  // if 10 guesses have been had
  lastResult.innerHTML = "!!!GAME OVER!!!";
  // provide a game over message
} else {
  
  // place the existing if...else here

}

If the game is over, then let the player know. If not, then carry on with the game.

When the number is guessed correctly, the game should end and no more guesses should be allowed.

This is a big issue: when the number is guessed correctly, we no longer want users to be able to enter more guesses, otherwise it totally breaks the flow of the game. There are a lot of different options that could be chosen to rectify this. We will add a function called disableForm() that stops the form being clickable when run. We will call this function when either the 10th guess is reached, or the number is guessed correctly.

first of all, add the disableForm() function somewhere near the bottom of the code. Just above the .onclick line will be fine:

function disableForm() {
  var wholeForm = document.querySelector(".form");
  // grab a reference to the whole form (the contents of the
  // div with class form)
  wholeForm.style.opacity = 0.5;
  // change the opacity of the form to 0.5
  guessField.setAttribute("disabled", "disabled");
  guessSubmit.setAttribute("disabled", "disabled");
  // disable the form field and submit button so they can
  // no longer be used
}

Now invoke this function in appropriate places using disableForm(); — you want to invoke it if guessCount === 10, and if userGuess === randomNumber, to give you a large clue.

Note: As an extra advanced exercise, you could try adding a button that when clicked starts a new game by resetting the state of the program and begins it all again.


Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. Share it, make it better, use it for good.