Home >Web Front-end >CSS Tutorial >Creating a Dynamic Hangman Game with JavaScript: A Technical Overview
Introduction
MTnD Hangman game, a classic word-guessing game, serves as an excellent project to practice and showcase various web development skills. In this project, I developed a Hangman game with enhanced features, including tracking the number of trials, providing clues, displaying trial counts, updating images after failed attempts, and displaying a congratulatory message for correct guesses. The game was deployed on Vercel, allowing for easy sharing and access.
This game demo can be assessed here
Features
Technologies Used
HTML: Structure of the Game
The HTML structure is straightforward, with a few divs and elements to display the game’s components like empty image tags , heading tags and audio tags to be accessible for toggling different game states.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Hangman</title> <link rel="stylesheet" href="style.css" /> </head> <body> <div id="game-container"> <div class="togglemode"></div> <h3 id="tries"></h3> <h1>MTnD Hangman</h1> <h5 id="clue"></h5> <audio src="" id="hangman-aud" volume="9"></audio> <div id="word-container"></div> <div id="letters-container"></div> <img alt="Hangman Image" id="hangman-img" /> <p id="message"></p> <button id="restart-btn">Restart Game</button> </div> <script src="script.js"></script> </body> </html>
CSS: Styling the Game
The CSS styles enhance the visual appeal and responsiveness of the game:
* { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: Arial, sans-serif; line-height: 1.6; text-align: center; justify-content: center; display: flex; color: #333; background: linear-gradient(to bottom, #a8edea, #fed6e3); align-items: center; margin: 0; height: 100vh; /* background-color: #f9f9f9; */ } #game-container { display: flex; flex-direction: column; width: 80%; margin: 50px auto; padding: 20px; border: 1px solid #ccc; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* justify-content: center; */ align-items: center; background: rgba(255, 255, 255, 0.8); text-align: center; } h1 { font-family: "Pacifico", cursive; color: #ff6f61; } #word-container { display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: bold; margin-bottom: 20px; } .logo { height: 80px; width: 83px; } .letter-btn { margin: 10px; padding: 10px 20px; border: none; border-radius: 8px; color: #fff; background: #ff6f61; cursor: pointer; transition: background 0.3s ease; } .letter-btn:hover { background-color: #ff402e; } .letter-btn.disabled { background-color: #ccc; cursor: not-allowed; } #message { font-size: 18px; font-weight: bold; color: #666; margin-bottom: 20px; } #restart-btn { font-weight: bold; padding: 10px 20px; border: none; border-radius: 10px; background-color: #ff6f61; color: #fff; cursor: pointer; margin-bottom: 20px; } #restart-btn:hover { background-color: #ff402e; } #hangman-img { width: 180px; height: 180px; margin: 0 20px; transition: transform 0.3s ease-in-out; } .hangman-image:hover { transform: scale(1.05); } #clue { font-size: 18px; font-weight: bold; margin-bottom: 20px; color: darkblue; } #tries { position: relative; left: 30%; margin: 10px; /* margin-left: 900px; */ padding: 10px 20px; border: none; border-radius: 10px; background-color: #4fd8d8; color: #fff; cursor: pointer; } /* Media Queries */ /* Small screens (max-width: 768px) */ @media (max-width: 768px) { #game-container { display: flex; flex-direction: column; width: 90%; margin: 20px auto; height: 100vh; padding: 10px; align-items: center; } #word-container { font-size: 18px; } .letter-btn { margin: 5px; padding: 5px 10px; } #letters-container { width: 350px; } #message { font-size: 14px; font-weight: bold; } #restart-btn { height: 30px; padding: 5px 10px; } #hangman-img { width: 120px; height: 120px; } #clue { font-size: 14px; } #tries { /* margin: 5px; */ left: 30%; padding: 13px 5px 10px 5px; } } /* Extra small screens (max-width: 480px) */ @media (max-width: 480px) { #game-container { display: flex; flex-direction: column; width: 100%; height: 100vh; margin: 10px auto; padding: 5px; align-items: center; background:#ebfcfc; } #letters-container { /* width: 280px; */ flex-wrap: wrap; margin-bottom: 30px; } #word-container { font-size: 20px; } .letter-btn { height: 30px; width: 30px; border-radius: 100%; margin: 2px; padding: 2px 5px; } #message { font-weight: bold; font-size: 30px; margin: 10px 0 10px 0; } #restart-btn { margin-top: 30px; height: 40px; font-size: 20px; padding: 2px 5px; } #hangman-img { width: 170px; height: 170px; margin: 30px 0 0px 0; } #clue { margin-top: 40px; font-size: 21px; } #tries { left: 9%; width: 150px; flex-wrap: wrap; margin: 20px 0 40px 0; padding: 15px; margin-left: 170px; } } @media (max-width: 320px) { #game-container { display: flex; flex-direction: column; width: 100%; height: 100vh; margin: 10px auto; padding: 5px; align-items: center; background:#ebfcfc; } #letters-container { width: 270px; flex-wrap: wrap; margin-bottom: 20px; } #word-container { font-size: 20px; } .letter-btn { margin: 2px; padding: 2px 5px; } #message { font-weight: bold; font-size: 15px; } #restart-btn { font-size: medium; padding: 2px 5px; } #hangman-img { width: 120px; height: 120px; margin: 15px 0 0px 0; } #clue { margin-top: 10px; font-size: 18px; } #tries { left: 20%; margin: 2px; padding: 15px 0 0 0; } }* { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: Arial, sans-serif; line-height: 1.6; text-align: center; justify-content: center; display: flex; color: #333; background: linear-gradient(to bottom, #a8edea, #fed6e3); align-items: center; margin: 0; height: 100vh; /* background-color: #f9f9f9; */ } #game-container { display: flex; flex-direction: column; width: 80%; margin: 50px auto; padding: 20px; border: 1px solid #ccc; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* justify-content: center; */ align-items: center; background: rgba(255, 255, 255, 0.8); text-align: center; } h1 { font-family: "Pacifico", cursive; color: #ff6f61; } #word-container { display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: bold; margin-bottom: 20px; } .logo { height: 80px; width: 83px; } .letter-btn { margin: 10px; padding: 10px 20px; border: none; border-radius: 8px; color: #fff; background: #ff6f61; cursor: pointer; transition: background 0.3s ease; } .letter-btn:hover { background-color: #ff402e; } .letter-btn.disabled { background-color: #ccc; cursor: not-allowed; } #message { font-size: 18px; font-weight: bold; color: #666; margin-bottom: 20px; } #restart-btn { font-weight: bold; padding: 10px 20px; border: none; border-radius: 10px; background-color: #ff6f61; color: #fff; cursor: pointer; margin-bottom: 20px; } #restart-btn:hover { background-color: #ff402e; } #hangman-img { width: 180px; height: 180px; margin: 0 20px; transition: transform 0.3s ease-in-out; } .hangman-image:hover { transform: scale(1.05); } #clue { font-size: 18px; font-weight: bold; margin-bottom: 20px; color: darkblue; } #tries { position: relative; left: 30%; margin: 10px; /* margin-left: 900px; */ padding: 10px 20px; border: none; border-radius: 10px; background-color: #4fd8d8; color: #fff; cursor: pointer; } /* Media Queries */ /* Small screens (max-width: 768px) */ @media (max-width: 768px) { #game-container { display: flex; flex-direction: column; width: 90%; margin: 20px auto; height: 100vh; padding: 10px; align-items: center; } #word-container { font-size: 18px; } .letter-btn { margin: 5px; padding: 5px 10px; } #letters-container { width: 350px; } #message { font-size: 14px; font-weight: bold; } #restart-btn { height: 30px; padding: 5px 10px; } #hangman-img { width: 120px; height: 120px; } #clue { font-size: 14px; } #tries { /* margin: 5px; */ left: 30%; padding: 13px 5px 10px 5px; } } /* Extra small screens (max-width: 480px) */ @media (max-width: 480px) { #game-container { display: flex; flex-direction: column; width: 100%; height: 100vh; margin: 10px auto; padding: 5px; align-items: center; background:#ebfcfc; } #letters-container { /* width: 280px; */ flex-wrap: wrap; margin-bottom: 30px; } #word-container { font-size: 20px; } .letter-btn { height: 30px; width: 30px; border-radius: 100%; margin: 2px; padding: 2px 5px; } #message { font-weight: bold; font-size: 30px; margin: 10px 0 10px 0; } #restart-btn { margin-top: 30px; height: 40px; font-size: 20px; padding: 2px 5px; } #hangman-img { width: 170px; height: 170px; margin: 30px 0 0px 0; } #clue { margin-top: 40px; font-size: 21px; } #tries { left: 9%; width: 150px; flex-wrap: wrap; margin: 20px 0 40px 0; padding: 15px; margin-left: 170px; } } @media (max-width: 320px) { #game-container { display: flex; flex-direction: column; width: 100%; height: 100vh; margin: 10px auto; padding: 5px; align-items: center; background:#ebfcfc; } #letters-container { width: 270px; flex-wrap: wrap; margin-bottom: 20px; } #word-container { font-size: 20px; } .letter-btn { margin: 2px; padding: 2px 5px; } #message { font-weight: bold; font-size: 15px; } #restart-btn { font-size: medium; padding: 2px 5px; } #hangman-img { width: 120px; height: 120px; margin: 15px 0 0px 0; } #clue { margin-top: 10px; font-size: 18px; } #tries { left: 20%; margin: 2px; padding: 15px 0 0 0; } }
JavaScript
was used majorly for the conditionals and interactivity of the game.
const languages = ["javascript", "python", "java", "ruby"]; const frameworks = ["react", "angular", "vue", "django", "flask"]; const tools = ["git", "webpack", "babel", "eslint", "prettier"]; const concept = ["closure", "callback", "promises", "async", "hosting"]; const databases = ["mongodb", "sqlite", "mysql"]; const allObjects = {languages, frameworks, tools, concept, databases}; let chosenWord = ""; let guessedLetters = []; let wrongGuesses; const wordContainer = document.getElementById("word-container"); const lettersContainer = document.getElementById("letters-container"); const message = document.getElementById("message"); const restartBtn = document.getElementById("restart-btn"); const hangmanImg = document.getElementById("hangman-img"); const hangmanAud = document.getElementById("hangman-aud"); const trials = document.getElementById("tries"); const clue = document.getElementById("clue"); function init() { const randomArray = Object.values(allObjects)[ Math.floor(Math.random() * Object.keys(allObjects).length) ]; const randomValue = randomArray[Math.floor(Math.random() * randomArray.length)]; console.log(randomValue); const getClue = () => { for (const [key, value] of Object.entries(allObjects)) { if (value.includes(randomValue)) { return key; } } }; clue.textContent = `Clue: "${getClue().toUpperCase()}" in Programming`; chosenWord = randomValue; // words[Math.floor(Math.random() * words.length)]; guessedLetters = []; remainingGuesses = 5; message.textContent = ""; wordContainer.innerHTML = "_ ".repeat(chosenWord.length).trim(); lettersContainer.innerHTML = ""; hangmanImg.src = "hangmanSteady.png"; trials.innerText = "YOU HAVE 5 TRIALS!"; wrongGuesses = 0; for (let i = 65; i <= 90; i++) { const letterBtn = document.createElement("button"); letterBtn.classList.add("letter-btn"); letterBtn.textContent = String.fromCharCode(i); letterBtn.addEventListener("click", handleGuess); lettersContainer.appendChild(letterBtn); } } //after click this disables all buttons function disableAllButtons() { const buttons = document.querySelectorAll(".letter-btn"); buttons.forEach((button) => { button.classList.add("disabled"); button.disabled = true; }); } restartBtn.addEventListener("click", init); init(); //this handles guesses function handleGuess(event) { const letter = event.target.innerText.toLowerCase(); event.target.classList.add("disabled"); event.target.disabled = true; if (chosenWord.includes(letter)) { guessedLetters.push(letter); const displayWord = chosenWord .split("") .map((letter) => (guessedLetters.includes(letter) ? letter : "_")) .join(" "); wordContainer.textContent = displayWord; } else { wrongGuesses++; if (wrongGuesses === 1) { trials.innerText = "4 trials left"; hangmanImg.src = "hangman1.png"; hangmanAud.src = "failed.mp3"; hangmanAud.play(); } else if (wrongGuesses === 2) { hangmanImg.src = "hangman2.png"; trials.innerText = "3 trials left"; hangmanAud.src = "failed.mp3"; hangmanAud.play(); } else if (wrongGuesses === 3) { hangmanImg.src = "hangman3.png"; trials.innerText = "2 trials left"; hangmanAud.src = "failed.mp3"; hangmanAud.play(); } else if (wrongGuesses === 4) { hangmanImg.src = "hangman4.png"; trials.innerText = "1 trials left"; hangmanAud.src = "failed.mp3"; hangmanAud.play(); } } handleGameOver(); } const handleGameOver = () => { if (wrongGuesses === 5) { message.textContent = `Game Over! ❌ The word was "${chosenWord}".`; disableAllButtons(); hangmanImg.src = "hangmanFailed.png"; trials.innerText = "0 TRIAL!"; hangmanAud.src = "gameover.mp3"; hangmanAud.play(); document.querySelector("#message").style.color = "red"; } if (chosenWord.split("").every((letter) => guessedLetters.includes(letter))) { message.textContent = "Congratulations! You guessed the word!"; hangmanImg.src = "hangmanSuccess.png"; trials.innerText = "Congrats!"; hangmanAud.src = "success.mp3"; hangmanAud.play(); document.querySelector("#message").style.color = "green"; disableAllButtons(); } };
Code Explanation;
I created an object of arrays. The array names serve as the clue to the word. The game is such that it randomly loops into the objects then gets a single array, for example if after it loops and finally gets the languages array. It then randomly loops over the language array and hides the guess. Hence the player only has a clue of what is to be guessed . So if the guessed word is “python”. The player gets to attempt to guess the word. If after 5 failed attempts of guesses, you failed the game and will have to restart. But if you get the word without 5 failed attempts you get a congratulation message.
The addition of images and audio makes the game intriguing making it much more interactive.
Conclusion
Developing this Hangman game was a rewarding experience that allowed me to apply and enhance my skills in JavaScript. The game’s features, including trial tracking, clues, dynamic images, and feedback messages, create an engaging and interactive experience for players. Deploying the game on Vercel ensures it is accessible and shareable, showcasing the capabilities of modern web development tools and practices.
Future Advancement.
I would be looking forward to adding new characteristics to the game in the future to implement.
A score keeper section: it basically stores the amount of success and failures and it returns the total score over 10. For example if you get 6 wrong attempts you get 4/10.
Timer ⌛: I implement a timer, for example for each guess you get 15 seconds if after 5 second you fail to input a word, you automatically get a failed attempt. To improve the difficulty of the game.
Future implementation
The MTnD Hangman game can be used in different niches, only left for me to tailor the objects to your niche, the only things I’ll be needing are keywords that align with your specific niche. The MTnD hangman could be applied in the following;
Each niche could provide a unique twist to the MTnD Hangman game and attract different audiences.
I would be open to suggestions on this project.
The above is the detailed content of Creating a Dynamic Hangman Game with JavaScript: A Technical Overview. For more information, please follow other related articles on the PHP Chinese website!