Table of Contents
At some point in your life, you would have played the classic Guess the Number game. This is a game where one person has a number in their mind. It is up to you to guess what that number is, and let's learn how to play it by...actually playing it! You can play this game below (or open it in a new window):
Your goal is to guess the number the game has in mind. The rules of this game are straightforward. For every number you guess, if you didn't guess the number correctly, you will be told whether your guess is greater than or less than the actual number the game has in mind. With that information, you make another guess to get closer to the answer.
The idea is that, with each answer on where your guess falls, you can narrow down on the correct answer until you eventually guess the correct answer exactly:
Seems like a fun and simple game, right? What you are going to do in this exercise is re-create this game.
Onwards!
The easiest way is to fork the following Codepen pen and start adding your modifications:
See the Pen Coding Exercises Start by Kirupa Chinnathambi (@kirupa) on CodePen.
You may want to open the pen in a new window if you want more space to code your solution or bring up the Console to see the output.
If you prefer developing locally in your favorite code editor (like Visual Studio Code), create a new HTML document and copy/paste the following boilerplate/starting content into it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Coding Exercises Start</title>
</head>
<body>
<script>
</script>
</body>
</html>
The HTML you see here is just the bare minimum content needed to help us get our web page up and running. The rest of the content is what you will add entirely on your own!
Once you have completed this challenge, you have earned the awesome bragworthy privilege of adding the following badge to your collection:
To claim it, head over to the forums and respond in the Guess the Number Game topic. Be sure to include a link to your solution or insert a copy of your HTML/CSS/JS in the body of the message:
Once you have created your topic, Kirupa will give you a virtual high-five and ensure this badge is added to your list of assigned badges.
We want to make this a fun learning activity. If you are stuck and need help, please ask on the forums. Please explain your problem in detail and one of the many helpful forum members will help you out.
If you want to see one way of solving this, check out Kirupa's video and article below:
Before we write the first line of code, it is helpful to take a step back and think about what is going on in this game. If we start with the UI, the primary elements are the input field where we enter our guess and a scrollable results area below where we see whether:
There are some other visual elements like the picture of a hamburger and the game title itself that round out all of the interesting parts that we see:
Digging deeper, beneath the UI is the game logic itself. This is where we have some JavaScript to ensure the number we enter is processed, compared to the answer value, and the appropriate results are generated and displayed in the scrollable results area. Let's turn all of these words and explanations into a working app in the next few sections. The best way for you to learn is to take a moment and try to create this game yourself. Once you have made sufficient progress (or gotten stuck), you can read on and follow along with how I would build this.
One way to get started building this app is by focusing on the UI first and then making one's way down tothe game logic. We are going to spice things up a bit and do things differently. The way we are going to start building this app is by starting with the core logic needed to make our game work first. Only after we get the game's core capabilities working will we shift our focus to the UI.
If you want to actively following along, create a new HTML document and add the following contents into it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guess Number (Tutorial)</title>
</head>
<body>
<script>
let numberToGuess = 10;
let enteredNumber = 15;
function checkNumber() {
if (enteredNumber == numberToGuess) {
console.log("You win!");
return;
}
if (enteredNumber < numberToGuess) {
console.log("Your guess is too low!");
}
if (enteredNumber > numberToGuess) {
console.log("Your guess is too high!");
}
}
checkNumber();
</script>
</body>
</html>
Take a moment to look at what this page is doing. Outside of the basic HTML boilerplate content, we have our checkNumber function that checks the value of enteredNumber against numberToGuess:
function checkNumber() {
if (enteredNumber == numberToGuess) {
console.log("You win!");
return;
}
if (enteredNumber < numberToGuess) {
console.log("Your guess is too low!");
}
if (enteredNumber > numberToGuess) {
console.log("Your guess is too high!");
}
}
checkNumber();
For the purposes of what we are doing, the values of enteredNumber and numberToGuess are hardcoded to 15 and 10 respectively. If you run this example in the browser, bring up the Console to see the Your guess is too high! output:
To test what our game will do for other values, change the value for enteredNumber in our example to other values like 9 or 10 to see our checkNumber function print the other messages to our console. Each time you change the value of enteredNumber in your code editor, you do have to refresh the page to ensure the new value gets picked up. That's the temporary price of starting from a very blank slate!
From this humble starting point, the first thing we'll do is replace the messages being printed to our console to having the messages show up in our page. To do this, make the following highlighted additions and modifications to our page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guess Number (Tutorial)</title>
</head>
<body>
<div class="main">
<p id="results"></p>
</div>
<script>
let numberToGuess = 10;
let enteredNumber = 15;
let results = document.querySelector("#results");
function checkNumber() {
if (enteredNumber == numberToGuess) {
let message = `<p>----- New Game Time -----</p>
<p>🎉 Your guess of ${enteredNumber} is <b>correct</b>!</p>
<p>-------------------------<p>`;
results.insertAdjacentHTML("afterbegin", message);
return;
}
if (enteredNumber < numberToGuess) {
let message = `<p>⚓ Your guess of ${enteredNumber} is <b>too low</b>!</p>`;
results.insertAdjacentHTML("afterbegin", message);
}
if (enteredNumber > numberToGuess) {
let message = `<p>🎈 Your guess of ${enteredNumber} is <b>too high</b>!</p>`;
results.insertAdjacentHTML("afterbegin", message);
}
}
checkNumber();
</script>
</body>
</html>
We added some HTML that will act as the parent for displaying our results, and we created the results value in JavaScript to store a reference to our #results HTML element. We also replaced our console.log statements to print the messages to our HTML element itself. After making these changes, go back to the browser and see what the new changes look like. You will see the results of our guesses appearing in our HTML document (as opposed to the Console):
Notice what I'm doing in the Console. I'm overwriting the numberToGuess variable and calling checkNumber again to run different code paths inside our checkNumber function. This is different than what we did earlier where we changed the value of numberToGuess in code and refreshed the page.
Next, let's add our input field and submit button to allow us to enter our guesses without writing and overwriting the numberToGuess variable directly. This will require making the following changes to our HTML and JavaScript:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guess Number (Tutorial)</title>
</head>
<body>
<div class="main">
<form onsubmit="return false">
<input placeholder="enter a number" inputmode="decimal" id="numberField" type="number">
<input type="submit" value="Guess" id="guessButton">
</form>
<p id="results"></p>
</div>
<script>
let numberToGuess = 10;
let numberField = document.querySelector("#numberField");
let guessButton = document.querySelector("#guessButton");
let results = document.querySelector("#results");
guessButton.addEventListener("click", checkNumber, false);
function checkNumber(event) {
let enteredNumber = numberField.value;
numberField.value = "";
if (enteredNumber == numberToGuess) {
let message = `<p>----- New Game Time -----<p>
<p>🎉 Your guess of ${enteredNumber} is <b>correct</b>!</p>
<p>-------------------------<p>`;
results.insertAdjacentHTML("afterbegin", message);
return;
}
if (enteredNumber < numberToGuess) {
let message = `<p>⚓ Your guess of ${enteredNumber} is <b>too low</b>!</p>`;
results.insertAdjacentHTML("afterbegin", message);
}
if (enteredNumber > numberToGuess) {
let message = `<p>🎈 Your guess of ${enteredNumber} is <b>too high</b>!</p>`;
results.insertAdjacentHTML("afterbegin", message);
}
}
checkNumber();
When you preview the latest changes in the browser, you'll now see an input field and associated button. Submit some guesses to see the appropriate result show-up depending on whether your guess was less than, greater than, or equal to the current hard-coded guess of 10:
Our game is starting to come along, right? There are a bunch more loose ends we need to tie up, so we are going to keep on keeping on.
The first thing we want to do is replace our hardcoded guess value of 10 to something more random. To do this, make the following highlighted changes to our JavaScript where we add the setNumberToGuess and getRandomNumber functions that set our numberToGuess variable to a random number between 0 and 100:
let numberToGuess;
let numberField = document.querySelector("#numberField");
let guessButton = document.querySelector("#guessButton");
let results = document.querySelector("#results");
guessButton.addEventListener("click", checkNumber, false);
function setNumberToGuess() {
numberToGuess = getRandomNumber(0, 100);
}
setNumberToGuess();
function getRandomNumber(low, high) {
let r = Math.floor(Math.random() * (high - low + 1)) + low;
return r;
}
function checkNumber(event) {
let enteredNumber = numberField.value;
numberField.value = "";
if (enteredNumber == numberToGuess) {
let message = `<p>----- New Game Time -----<p>
<p>🎉 Your guess of ${enteredNumber} is <b>correct</b>!</p>
<p>-------------------------<p>`;
results.insertAdjacentHTML("afterbegin", message);
setNumberToGuess();
return;
}
if (enteredNumber < numberToGuess) {
let message = `<p>⚓ Your guess of ${enteredNumber} is <b>too low</b>!</p>`;
results.insertAdjacentHTML("afterbegin", message);
}
if (enteredNumber > numberToGuess) {
let message = `<p>🎈 Your guess of ${enteredNumber} is <b>too high</b>!</p>`;
results.insertAdjacentHTML("afterbegin", message);
}
}
checkNumber();
Notice that we also specify a call to setRandomNumber inside our checkNumber function for the case where we guess the answer correctly. This will ensure we start a new game where the value for numberToGuess is a new random number.
The last thing we will do to our JavaScript is remove the call to checkNumber that is our last line of code just above the closing script tag:
This line was needed when we were testing our app and had no way of calling checkNumber proactively, but that time was several paragraphs ago. Those days are long gone! Removing this line now will ensure our game doesn't automatically make a guess without us entering/submitting a number.
At this point, our game should be mostly functional! We can enter a guess, adjust based on the results, and keep guessing until we guess correctly. The game will then restart with a new value available for us to guess. What is missing is the visual flair. What is missing is the all-important CSS.
Our styling game here will be fairly basic, and the styles we'll be adding will be made up of your run-of-the-mill properties and values. Just above the closing head element, add the following style tag and its CSS contents:
<style>
body {
font-family: monospace;
background-color: #FDE74C;
}
.main {
display: grid;
justify-content: center;
justify-items: center;
}
.main img {
width: 100px;
}
.main h1 {
font-size: 48px;
margin: 20px;
text-align: center;
}
.main p {
font-size: 20px;
line-height: 40px;
width: -webkit-fill-available;
text-align: center;
}
#results {
max-height: 225px;
overflow: auto;
background-color: hsl(0deg 0% 100% / 44%);
border-radius: 10px;
}
.main form {
display: grid;
grid-template-columns: 1fr 120px;
width: 100%;
}
.main form * {
padding: 10px;
margin: 10px;
font-family: monospace;
font-weight: bold;
font-size: 18px;
border: 3px solid black;
border-radius: 10px;
}
#guessButton {
background-color: #b2deff;
cursor: pointer;
}
#guessButton:hover {
background-color: #88c9fa;
}
</style>
If you refresh your page now, you will now see our game in a near final form with it adopting the look and feel we just defined in CSS:
What is missing is the logo and title, so let's quickly add both of those to our HTML:
<div class="main"> <img src="https://www.kirupa.com/icon/1f369.svg"> <h1>Guess the Number</h1> <form onsubmit="return false"> <input placeholder="enter a number" id="numberField"
inputmode="decimal"
type="number"> <input type="submit" value="Guess" id="guessButton"> </form> <p id="results"></p> </div>
After you add these two highlighted lines, refresh the game in your browser again. The styles to make our logo and title look appropriate were added earlier, so there isn't anything extra we need to do. At this point, our game should look nearly finished:
Now that we have our game looking and working as expected, there is just one more thing that we need to do before we can safely wrap this up. When we submit our form, we don't check to see if someone has actually specified a guess. To fix that, let's check that something has been entered by adding the following JavaScript towards the top of our checkNumber function:
function checkNumber(event) {
let enteredNumber = numberField.value;
numberField.value = "";
if (enteredNumber == "") {
let message = `<p>⛔ Guess a number first!</p>`;
results.insertAdjacentHTML("afterbegin", message);
return;
}
.
.
.
}
After this change, if someone guesses without entering a number, we will display a message indicating that they need to specify a number first, and we exit the checkNumber function via the return keyword.
And with this change, we are in a good spot. Our game looks and works as expected. Before we wrap things and call it a day, let's take a few moments in the next section to walk through some of the interesting pieces of HTML, CSS, and JS that we have.
Our game seems rather simple, but as you may have noticed from building it in the previous few sections, there are some techniques that we used that won't seem very obvious at first glance. Let's take a look at those in more detail.
The way we submit a guess by entering a number in our input field and either pressing the Guess button or pressing the Submit/Enter key. This behavior is automatically handled for us because our Guess button is of type submit:
<form onsubmit="return false">
<input placeholder="enter a number" id="numberField" type="number">
<input type="submit" value="Guess" id="guessButton">
</form>
When we rely on our browser's default form submission behavior, we get an unintended side-effect. The entire page will refresh. This will cause any data you have already entered to get lost, and our game will have started again with a new number for us to guess. We'll never get past the first guess. The way we can avoid this is to override the default behavior where the page reloads when the form is submitted. Take a look at the following highlighted line for how we can do that:
<form onsubmit="return false">
<input placeholder="enter a number" id="numberField" type="number">
<input type="submit" value="Guess" id="guessButton">
</form>
By having onsubmit="return false", we tell our browser to ignore the submission behavior. The reason is that we have our own code for taking the number once its been submitted and making it a part of our game logic:
guessButton.addEventListener("click", checkNumber, false);
Yes, submitting our form (even by pressing the Enter/Return key) will trigger the click event, and this event handler will get triggered with our checkNumber function getting called.
When we are displaying our results, we insert some HTML that gets displayed ahead of the results from previous guesses. The most common approach we would have for inserting HTML is by relying on the innerHTML property:
let parentElement = document.querySelector("#foo");
parentElement.innerHTML += "Content 1";
parentElement.innerHTML += "Content 2";
parentElement.innerHTML += "Content 3";
parentElement.innerHTML += "Content 4";
What innerHTML does is append new content to the end. It doesn't have a mechanism for adding content to a beginning of a list of elements. To get the behavior we want where our HTML will display the latest results ahead of previous results, we need to use insertAdjacentHTML:
if (enteredNumber < numberToGuess) {
let message = `<p>⚓ Your guess of ${enteredNumber} is <b>too low</b>!</p>`;
results.insertAdjacentHTML("afterbegin", message);
}
The insertAdjacentHTML method takes a few arguments. The first argument specifies exactly where the newly inserted HTML content will live, and specifying a value of afterbegin ensures it appears ahead of any existing content. The second argument is the content we are inserting. This method seems like a much more powerful version of innerHTML!
Now, I have to admit that I didn't know about this method until I ran into this situation where I needed to have our newly added HTML appear ahead of existing HTML. If that describes you as well, check out the MDN documentation that has all the details about this magical method.
When we play this game on a mobile device or any device that brings up an on-screen keyboard, setting the focus on the input element displays the numerical keyboard:
The way we can ensure the numerical keyboard shows up as opposed to the regular keyboard is by setting the type and inputmode attributes on our input element:
<form onsubmit="return false">
<input placeholder="enter a number" inputmode="decimal" id="numberField" type="number">
<input type="submit" value="Guess" id="guessButton">
</form>
The type attribute with the number value and the inputmode attribute with the decimal value ensure that, if the on-screen keyboard is displayed, the keys are optimized for numerical input.
Wohoo! We just finished building the classic Guess the Number game. Hopefully you agree that what we built here didn't cut corners. We built a high-quality game without a whole lot of HTML, CSS, and JavaScript. There are always additional things we can do (sounds? animations? moar???), so please do chime in on the forums if you have thoughts on how we can make this game better!
Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!
:: Copyright KIRUPA 2024 //--