The JavaScript guessing game tutorial is a simple beginner’s project. It features modern JavaScript syntax and runs without a Framework. In part one of the game the foundation is built. The second part adds suspense by only giving the user five attempts to guess. In this third part, we have added the ability for the user to set the level and change part way through if you have any attempts left.
Table of contents
Prerequisites
A modern browser and a text editor. I recommend VS Code with the Live Server extension. Some knowledge of HTML and JavaScript would be beneficial but not required. Version 2.0 uses TypeScript and includes code from the previous two versions of the JavaScript Guessing Game. You can run the game from the dist folder. Have you missed the previous versions?
Easy JavaScript Guessing Game Part 1
Easy JavaScript Guessing Game Part 2
Prerequisites To Build
Suppose you want to make any changes, the source files are included. I have used Bun to manage the Bulma package. I have used Dart-Sass CLI to build the Bulma Stylesheet. The TypeScript CLI compiles the code to JavaScript.
Download
The latest JavaScript Guessing Game update has been released on 26th September 2024.
Also available on GitHub
Let’s Get Coding
Guessing Game Code
We have assigned constants for the minimum and maximum range. To manage the state between level selects we use Object.defineProperty
. The data is synced with the browser’s local storage.
Source
const max: number = 100;
const min: number = 1;
const currentLevel: string | null = localStorage.getItem("level");
const level_arr: string[] = ["10", "5", "2"];
/**
* Manages the state of the levels
*/
let diff: string = currentLevel ?? "5";
const level = {
_difficulty: diff,
};
Object.defineProperty(level, "difficulty", {
get: function () {
return this._difficulty;
},
set: function (value) {
this._difficulty = value;
},
});
One new helper function is a shortcut to grab the element by ID which is used multiple times throughout the codebase.
/**
* Shorthand Get Element By ID
*/
const getID = (id: string) => {
return document.getElementById(id);
};
When the browser loads the page it directs focus on the input directly to save having to click inside the input field. It calls two new functions to set the level and generate the computer guess.
/**
* Executes on browser load. Saves computer guess to browser session if not set.
*/
window.onload = () => {
const guessInput = getID("guess");
if (guessInput !== null) {
guessInput.focus();
}
setLevelActive();
generateComputerGuess();
};
/**
* Manages the switching of the difficulty buttons, it's state and saves to local storage
*/
const setLevelActive = () => {
let btnLevel = document.querySelectorAll(".btn-level");
if (currentLevel !== null) {
let levelIndex = level_arr.findIndex((value) => value === currentLevel);
btnLevel[levelIndex].classList.add("is-active");
} else {
btnLevel[1].classList.add("is-active");
localStorage.setItem("level", level._difficulty);
}
btnLevel.forEach((btn) => {
btn.addEventListener("click", (e) => {
const totalGuesses = countGuesses();
btnLevel?.forEach((btn) => btn.classList.remove("is-active"));
(e.target as HTMLElement).classList.add("is-active");
let value = (e.target as HTMLElement).textContent;
switch (value) {
case "Easy":
level._difficulty = "10";
break;
case "Medium":
level._difficulty = "5";
break;
case "Hard":
level._difficulty = "2";
break;
}
localStorage.setItem("level", level._difficulty);
if (totalGuesses > level._difficulty) {
notify(
"You have already had more guesses than the difficulty chosen. Choose another level or your game will end!"
);
}
});
});
};
/**
* Generates the computer guess and saves it to session storage
*/
const generateComputerGuess = () => {
let genGuess: number = Math.floor(Math.random() * (max - min) + min),
arr: [string, string | number] = ["computer-guess", genGuess],
session = getSession(arr);
if (session === null) {
addToSession(arr);
}
};
One new feature is notifications. Whenever there is an issue or an error a message will be passed to the notify function then it will display in the browser.
/**
* Create notifications
*/
const notify = (msg: string | null) => {
let notification = document.createElement("div");
let deleteButton = document.createElement("button");
deleteButton.addEventListener("click", removeNotification);
deleteButton.className = "delete";
notification.className = "notification";
notification.textContent = msg;
notification.prepend(deleteButton);
const heroHead = document.querySelector(".notify");
if (heroHead !== null) {
heroHead.appendChild(notification);
}
};
The subsequent removeNotifications()
function removes all the notifications from the DOM.
/**
* Removes notifications
*/
const removeNotification = () => {
let notification = document.querySelectorAll(".notification");
notification.forEach((item) => {
item.remove();
});
};
The event listener on the submit button has had a few changes since the original version. It will display an error if the value is empty/null or the number submitted is not between 1 and 100. If the value has already been picked it will also issue a warning without incrementing the count.
/*
* Adds event listener to the submit button
*/
const btnElement = getID("btn");
if (btnElement !== null) {
btnElement.addEventListener("click", (e) => {
e.preventDefault();
let userGuess: HTMLInputElement | null = getID("guess") as HTMLInputElement,
usrArr: (string | number)[] = [],
data = getSession(["user-guess", null]);
if (parseInt(userGuess.value) < min || parseInt(userGuess.value) > max) {
notify(`Please enter a number between ${min} and ${max}`);
return;
}
if (userGuess !== null && userGuess.value === "") {
notify("Please enter a number");
return;
}
if (data?.includes(userGuess.value)) {
notify("Number already picked");
return;
}
usrArr = ["user-guess", userGuess.value];
checkGuess(usrArr);
displayGuesses();
userGuess.value = "";
});
}
The following 6 functions remain in Version 2.0. They are pretty self-explanatory.
getSession
addToSession
checkGuess
higherOrLower
countGuesses
clearSession
/**
* Get data out of session storage
*/
const getSession = (item: [string, string | number] | (string | null)[]) => {
let store: string = item[0] ?? "";
const storedItem = sessionStorage.getItem(store);
return storedItem !== null ? JSON.parse(storedItem) : null;
};
/**
* Stores data in sessionStorage. Merges data if already exists
*/
const addToSession = (item: any[]) => {
//Check if a session is set
let session = getSession(item),
data: (string | number)[] = [],
store: string = item[0],
saveData = item[1],
sessionData: (string | number)[] = [];
data.push(saveData);
// If the session return nothing
if (session === null) {
//create for the first time
sessionData = data;
} else {
//grab data and merge
session.push(data);
sessionData = session;
}
sessionStorage.setItem(store, JSON.stringify(sessionData.flat()));
};
/**
* Checks the user submitted number against the generated one
*/
const checkGuess = (guess: any[]) => {
addToSession(guess);
// Get user generated number
let generatedNumber = getSession(["computer-guess", null]),
message = getID("message"),
total_guesses = countGuesses();
if (total_guesses < parseInt(level._difficulty)) {
if (parseInt(guess[1]) !== generatedNumber[0]) {
higherOrLower(message, guess[1], generatedNumber[0]);
return;
} else if (message !== null) {
message.innerText =
"Jackpot, you won. You guessed it within " + total_guesses + " tries";
}
} else {
notify("You lose, you have reached the maximum guesses.");
}
clearSession();
};
/**
* Checks to see if your number matches the computer one
*/
const higherOrLower = (
message: HTMLElement | null,
num: string,
num2: number
) => {
if (message !== null) {
if (parseInt(num) > num2) {
message.innerText = "You need to go lower 👇";
} else {
message.innerText = "You need to go higher ☝️";
}
}
};
/**
* Counts the user guesses
*/
const countGuesses = () => {
let userGuess = getSession(["user-guess", null]),
guesses = userGuess !== null ? userGuess.length : 0;
return guesses;
};
/**
* Starts a new game by clearing a session and reloading the page
*/
const clearSession = () => {
setTimeout(() => {
sessionStorage.clear();
location.reload();
}, 5000);
};
Finally displayGuesses
displays the guesses that you have made.
/**
* Displays the user guesses in a tag format
*/
const displayGuesses = () => {
let guess = getSession(["user-guess", null]);
let tagsElement = getID("tags");
if (guess !== null && tagsElement !== null) {
tagsElement.textContent = "";
guess.forEach((item: string) => {
let span = document.createElement("span"),
text = document.createTextNode(item);
span.classList.add("tag");
span.appendChild(text);
tagsElement.appendChild(span);
});
}
};
The JavaScript Guessing Game V2.0 Demo
[codepen_embed height=”300″ default_tab=”html,result” slug_hash=”qBedNrX” pen_title=”Untitled” user=”ridgey28″]See the Pen
Untitled by Tracy Ridge (@ridgey28)
on CodePen.[/codepen_embed]
Conclusion
What started as a simple JavaScript Guessing Game has been transformed with new features including displaying notifications and selecting the difficulty level. Converting the project to TypeScript also alleviates issues with JavaScript to maximise performance. If you have any errors or want to suggest a new feature get in touch.
Discover more from WorldOWeb
Subscribe to get the latest posts sent to your email.