Skip to main content
Code Review

Return to Question

deleted 24 characters in body
Source Link
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 HINT_BUTTON.style.visibility = "hidden";
 hintUsed = true;
 const VIRTUAL_KEYS_CHILDREN = Array.from(
 VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHILDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = blanks.join("");
 const SECRET_WORD_STRING = secretWordArray.join("");

 BLANKS_TAG.textContent = secretWord;
 HINT_BUTTON.style.visibility = "hidden";
 NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRINGsecretWord) BLANKS_TAG.classList.add("correct");
 else BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 NEW_WORD_BUTTON.style.visibility = "hidden";
 BLANKS_TAG.classList.remove("correct", "incorrect");
 VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 secretWord = words[Math.floor(Math.random() * words.length)];
 trials = MAX_TRIALS;
 blanks = Array(secretWord.length).fill("_");
 secretWordArray = secretWord.split("");
 hintUsed = false;
 BLANKS_TAG.textContent = blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (trials === 0 || !blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
 if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 words = data.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 15px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p id="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 HINT_BUTTON.style.visibility = "hidden";
 hintUsed = true;
 const VIRTUAL_KEYS_CHILDREN = Array.from(
 VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHILDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = blanks.join("");
 const SECRET_WORD_STRING = secretWordArray.join("");

 BLANKS_TAG.textContent = secretWord;
 HINT_BUTTON.style.visibility = "hidden";
 NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
 else BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 NEW_WORD_BUTTON.style.visibility = "hidden";
 BLANKS_TAG.classList.remove("correct", "incorrect");
 VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 secretWord = words[Math.floor(Math.random() * words.length)];
 trials = MAX_TRIALS;
 blanks = Array(secretWord.length).fill("_");
 secretWordArray = secretWord.split("");
 hintUsed = false;
 BLANKS_TAG.textContent = blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (trials === 0 || !blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
 if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 words = data.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 15px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p id="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 HINT_BUTTON.style.visibility = "hidden";
 hintUsed = true;
 const VIRTUAL_KEYS_CHILDREN = Array.from(
 VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHILDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = blanks.join("");
 BLANKS_TAG.textContent = secretWord;
 NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === secretWord) BLANKS_TAG.classList.add("correct");
 else BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 NEW_WORD_BUTTON.style.visibility = "hidden";
 BLANKS_TAG.classList.remove("correct", "incorrect");
 VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 secretWord = words[Math.floor(Math.random() * words.length)];
 trials = MAX_TRIALS;
 blanks = Array(secretWord.length).fill("_");
 secretWordArray = secretWord.split("");
 hintUsed = false;
 BLANKS_TAG.textContent = blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (trials === 0 || !blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
 if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 words = data.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 15px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p id="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
deleted 24 characters in body
Source Link
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;words;
let secretWord;
let secretWordArray;

/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 HINT_BUTTON.style.visibility = "hidden";
 hintUsed = true;
 const VIRTUAL_KEYS_CHIDRENVIRTUAL_KEYS_CHILDREN = Array.from(
 VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHIDRENVIRTUAL_KEYS_CHILDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = blanks.join("");
 const SECRET_WORD_STRING = secretWordArray.join("");
 BLANKS_TAG.textContent = secretWord;
 HINT_BUTTON.style.visibility = "hidden";
 NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
 else BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 NEW_WORD_BUTTON.style.visibility = "hidden";
 BLANKS_TAG.classList.remove("correct", "incorrect");
 VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 secretWord = wordsArray[Mathwords[Math.floor(Math.random() * wordsArraywords.length)];
 trials = MAX_TRIALS;
 blanks = Array(secretWord.length).fill("_");
 secretWordArray = secretWord.split("");
 hintUsed = false;
 BLANKS_TAG.textContent = blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (trials === 0 || !blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
 if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 wordsArraywords = data.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 15px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p id="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;
let secretWord;
let secretWordArray;

/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 HINT_BUTTON.style.visibility = "hidden";
 hintUsed = true;
 const VIRTUAL_KEYS_CHIDREN = Array.from(
 VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHIDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = blanks.join("");
 const SECRET_WORD_STRING = secretWordArray.join("");
 BLANKS_TAG.textContent = secretWord;
 HINT_BUTTON.style.visibility = "hidden";
 NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
 else BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 NEW_WORD_BUTTON.style.visibility = "hidden";
 BLANKS_TAG.classList.remove("correct", "incorrect");
 VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 secretWord = wordsArray[Math.floor(Math.random() * wordsArray.length)];
 trials = MAX_TRIALS;
 blanks = Array(secretWord.length).fill("_");
 secretWordArray = secretWord.split("");
 hintUsed = false;
 BLANKS_TAG.textContent = blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (trials === 0 || !blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
 if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 wordsArray = data.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 15px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p id="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 HINT_BUTTON.style.visibility = "hidden";
 hintUsed = true;
 const VIRTUAL_KEYS_CHILDREN = Array.from(
 VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHILDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = blanks.join("");
 const SECRET_WORD_STRING = secretWordArray.join("");
 BLANKS_TAG.textContent = secretWord;
 HINT_BUTTON.style.visibility = "hidden";
 NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
 else BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 NEW_WORD_BUTTON.style.visibility = "hidden";
 BLANKS_TAG.classList.remove("correct", "incorrect");
 VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 secretWord = words[Math.floor(Math.random() * words.length)];
 trials = MAX_TRIALS;
 blanks = Array(secretWord.length).fill("_");
 secretWordArray = secretWord.split("");
 hintUsed = false;
 BLANKS_TAG.textContent = blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (trials === 0 || !blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
 if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 words = data.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 15px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p id="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
deleted 654 characters in body
Source Link
const gameDataBLANKS_TAG = {
 secretWord: "",
 MAX_TRIALS: 6,
 trials: 0,
 blanks: [],
 secretWordArray: [],
 hintUsed: false,
 words: "",
 WORDS_ARRAY: [],
 BLANKS_TAG: document.querySelectorgetElementById(".current"current-word"),;
const VIRTUAL_KEYS: = document.querySelectorgetElementById(".virtual"virtual-keys"),;
const HINT_BUTTON: = document.querySelectorgetElementById(".hint"hint-button"),;
const NEW_WORD_BUTTON: = document.querySelectorgetElementById(".new"new-word-button"),;
const HANGMAN_IMAGE: = document.querySelectorgetElementById("img""hangman-image"),
};
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;
let secretWord;
let secretWordArray;

/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 gameData.HANGMAN_IMAGE.src = `img/${
 gameData.MAX_TRIALS - gameData.trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = gameData.BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of gameData.secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 gameData.blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = gameData.blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 gameData.BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 gameData.trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 gameData.HINT_BUTTON.style.visibility = "hidden";
 gameData.hintUsed = true;
 const VIRTUAL_KEYS_CHIDREN = Array.from(
 gameData.VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHIDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !gameData.secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = gameData.blanks.join("");
 const SECRET_WORD_STRING = gameData.secretWordArray.join("");
 gameData.BLANKS_TAG.textContent = gameData.secretWord;
 gameData.HINT_BUTTON.style.visibility = "hidden";
 gameData.NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRING) gameData.BLANKS_TAG.classList.add("correct");
 else gameData.BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 gameData.NEW_WORD_BUTTON.style.visibility = "hidden";
 gameData.BLANKS_TAG.classList.remove("correct", "incorrect");
 gameData.VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 gameData.secretWord = gameData.WORDS_ARRAY[
 MathwordsArray[Math.floor(Math.random() * gameData.WORDS_ARRAYwordsArray.length)];
 gameData.trials = gameData.MAX_TRIALS;
 gameData.blanks = Array(gameData.secretWord.length).fill("_");
 gameData.secretWordArray = gameData.secretWord.split("");
 gameData.hintUsed = false;
 gameData.BLANKS_TAG.textContent = gameData.blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (gameData.trials === 0 || !gameData.blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (gameData.trials === 1 && !gameData.hintUsed) gameData.HINT_BUTTON.style.visibility = "visible";
 if (!gameData.trials || !gameData.blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 gameData.words = data;
 gameData.WORDS_ARRAYwordsArray = gameData.wordsdata.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === gameData.NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === gameData.VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === gameData.HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 015px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
 }
,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p class="currentid="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
const gameData = {
 secretWord: "",
 MAX_TRIALS: 6,
 trials: 0,
 blanks: [],
 secretWordArray: [],
 hintUsed: false,
 words: "",
 WORDS_ARRAY: [],
 BLANKS_TAG: document.querySelector(".current-word"),
 VIRTUAL_KEYS: document.querySelector(".virtual-keys"),
 HINT_BUTTON: document.querySelector(".hint-button"),
 NEW_WORD_BUTTON: document.querySelector(".new-word-button"),
 HANGMAN_IMAGE: document.querySelector("img"),
};
/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 gameData.HANGMAN_IMAGE.src = `img/${
 gameData.MAX_TRIALS - gameData.trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = gameData.BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of gameData.secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 gameData.blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = gameData.blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 gameData.BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 gameData.trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 gameData.HINT_BUTTON.style.visibility = "hidden";
 gameData.hintUsed = true;
 const VIRTUAL_KEYS_CHIDREN = Array.from(
 gameData.VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHIDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !gameData.secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = gameData.blanks.join("");
 const SECRET_WORD_STRING = gameData.secretWordArray.join("");
 gameData.BLANKS_TAG.textContent = gameData.secretWord;
 gameData.HINT_BUTTON.style.visibility = "hidden";
 gameData.NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRING) gameData.BLANKS_TAG.classList.add("correct");
 else gameData.BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 gameData.NEW_WORD_BUTTON.style.visibility = "hidden";
 gameData.BLANKS_TAG.classList.remove("correct", "incorrect");
 gameData.VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 gameData.secretWord = gameData.WORDS_ARRAY[
 Math.floor(Math.random() * gameData.WORDS_ARRAY.length)];
 gameData.trials = gameData.MAX_TRIALS;
 gameData.blanks = Array(gameData.secretWord.length).fill("_");
 gameData.secretWordArray = gameData.secretWord.split("");
 gameData.hintUsed = false;
 gameData.BLANKS_TAG.textContent = gameData.blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (gameData.trials === 0 || !gameData.blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (gameData.trials === 1 && !gameData.hintUsed) gameData.HINT_BUTTON.style.visibility = "visible";
 if (!gameData.trials || !gameData.blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 gameData.words = data;
 gameData.WORDS_ARRAY = gameData.words.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === gameData.NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === gameData.VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === gameData.HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 0 auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
 }

.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button">Hint?</button>
 <img/>
 <button type="button" class="btn new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p class="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;
let secretWord;
let secretWordArray;

/**
 * Updates the hangman image based on the number of attempts remaining.
 */
function updateHangmanImage() {
 HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
 * Updates the blanks with the correctly guessed letter and marks the
 * corresponding span element as correct.
 * @param {string} LETTER - The letter to guess.
 * @returns {boolean} Returns true if it's a good guess, false otherwise.
 */
function guessAndUpdateBlanks(LETTER) {
 const SPANS = BLANKS_TAG.querySelectorAll("span");
 let isGoodGuess = false;
 let lastCorrectSpan = null;
 for (const [I, CHAR] of secretWordArray.entries()) {
 if (CHAR === LETTER) {
 isGoodGuess = true;
 blanks[I] = LETTER;
 lastCorrectSpan = SPANS[I];
 }
 }
 if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
 if (isGoodGuess) return true;
 else return false;
}
/**
 * Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
 * @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
 * @param {string} LETTER - The guessed letter.
 */
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
 if (IS_GOOD_GUESS) {
 const BLANKS_STRING = blanks.join(" ");
 const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
 new RegExp(LETTER, "gi"),
 `<span class="correct">${LETTER}</span>`
 );
 BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
 } else {
 trials--;
 updateHangmanImage();
 }
}
/**
 * Provides a hint by disabling a number of letters that are not in the secret word.
 */
function hint() {
 HINT_BUTTON.style.visibility = "hidden";
 hintUsed = true;
 const VIRTUAL_KEYS_CHIDREN = Array.from(
 VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
 );
 const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
 const INDEXES = [];
 while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
 const RANDOM_INDEX = Math.floor(
 Math.random() * VIRTUAL_KEYS_CHIDREN.length
 );
 const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
 const LETTER = BUTTON.getAttribute("data-value");
 if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
 BUTTON.classList.add("disabled");
 INDEXES.push(RANDOM_INDEX);
 }
 }
}
/**
 * Checks the game result and displays the secret word accordingly.
 */
function checkGameResult() {
 const BLANKS_STRING = blanks.join("");
 const SECRET_WORD_STRING = secretWordArray.join("");
 BLANKS_TAG.textContent = secretWord;
 HINT_BUTTON.style.visibility = "hidden";
 NEW_WORD_BUTTON.style.visibility = "visible";
 if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
 else BLANKS_TAG.classList.add("incorrect");
}
/**
 * Initialize the game and choose a secret word at random.
 */
function initializeGame() {
 NEW_WORD_BUTTON.style.visibility = "hidden";
 BLANKS_TAG.classList.remove("correct", "incorrect");
 VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
 BUTTON.classList.remove("disabled");
 });
 secretWord = wordsArray[Math.floor(Math.random() * wordsArray.length)];
 trials = MAX_TRIALS;
 blanks = Array(secretWord.length).fill("_");
 secretWordArray = secretWord.split("");
 hintUsed = false;
 BLANKS_TAG.textContent = blanks.join(" ");
 updateHangmanImage();
}
/**
 * Manages the game event when a virtual key is clicked.
 * @param {Event} event - The click event object.
 */
function play(event) {
 if (trials === 0 || !blanks.includes("_")) return;
 const BUTTON = event.target;
 const LETTER = BUTTON.getAttribute("data-value");
 const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
 replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
 BUTTON.classList.add("disabled");
 if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
 if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
 .then((response) => response.text())
 .then((data) => {
 wordsArray = data.split(" ");
 initializeGame();
 })
 .catch((error) => {
 console.error(`Error reading word file: ${error}`);
 });
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET === NEW_WORD_BUTTON) {
 initializeGame();
 } else if (TARGET.parentNode === VIRTUAL_KEYS) {
 play(event);
 } else if (TARGET === HINT_BUTTON) {
 hint();
 }
});
body {
 max-width: 100vw;
 max-height: 100vh;
}
header {
 margin-top: 20px;
 margin-bottom: 20px;
}
h1 {
 text-align: center;
}
.game-stage {
 min-height: 350px;
 max-height: 350px;
 display: flex;
 align-items: center;
 justify-content: center;
}
.game-current-word {
 min-height: 50px;
 max-height: 50px;
 display: flex;
 justify-content: center;
 font-size: 22px;
 font-weight: bold; 
}
.virtual-keyboard {
 min-height: 80px;
 max-height: 80px;
 max-width: 100vw;
}
.virtual-keys {
 max-width: 600px;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 margin: 15px auto;
}
.btn {
 background: none;
 border: 0;
 background-color: #0066cc;
 box-shadow: 2px 2px 1px #036;
 color: #f3ffff;
 cursor: pointer;
 font: inherit;
 font-weight: bold;
 line-height: normal;
 overflow: visible;
 width: 35px;
 height: 35px;
 border-radius: 5px;
 display: flex;
 align-items: center;
 justify-content: space-evenly;
 padding: 0 10px;
 margin: 5px;
}
.hint-button,
.new-word-button {
 width: 40px;
 font-size: 12px;
 visibility: hidden;
}
.disabled {
 background-color: #fff;
 color: #000;
 pointer-events: none;
 cursor: not-allowed;
}
.correct {
 color: green;
 font-size: 25px;
}
.incorrect {
 color: red;
 font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Hangman game</title>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <link rel="stylesheet" type="text/css" href="assets/normalize.css">
 <link rel="stylesheet" type="text/css" href="assets/style.css" />
 <script src="assets/index.js" defer></script>
 </head>
 <body>
 <header><h1>Hangman game</h1></header>
 <section class="game-stage">
 <button type="button" class="btn hint-button" id="hint-button">Hint?</button>
 <img id="hangman-image"/>
 <button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
 </section>
 <section class="game-current-word">
 <p id="current-word"></p>
 </section>
 <section class="virtual-keyboard">
 <div class="virtual-keys" id="virtual-keys">
 <button type="button" class="btn" data-value="a">A</button>
 <button type="button" class="btn" data-value="b">B</button>
 <button type="button" class="btn" data-value="c">C</button>
 <button type="button" class="btn" data-value="d">D</button>
 <button type="button" class="btn" data-value="e">E</button>
 <button type="button" class="btn" data-value="f">F</button>
 <button type="button" class="btn" data-value="g">G</button>
 <button type="button" class="btn" data-value="h">H</button>
 <button type="button" class="btn" data-value="i">I</button>
 <button type="button" class="btn" data-value="j">J</button>
 <button type="button" class="btn" data-value="k">K</button>
 <button type="button" class="btn" data-value="l">L</button>
 <button type="button" class="btn" data-value="m">M</button>
 <button type="button" class="btn" data-value="n">N</button>
 <button type="button" class="btn" data-value="o">O</button>
 <button type="button" class="btn" data-value="p">P</button>
 <button type="button" class="btn" data-value="q">Q</button>
 <button type="button" class="btn" data-value="r">R</button>
 <button type="button" class="btn" data-value="s">S</button>
 <button type="button" class="btn" data-value="t">T</button>
 <button type="button" class="btn" data-value="u">U</button>
 <button type="button" class="btn" data-value="v">V</button>
 <button type="button" class="btn" data-value="w">W</button>
 <button type="button" class="btn" data-value="x">X</button>
 <button type="button" class="btn" data-value="y">Y</button>
 <button type="button" class="btn" data-value="z">Z</button>
 </div>
 </section>
 </body>
</html>
deleted 17 characters in body
Source Link
Loading
deleted 174 characters in body
Source Link
Loading
Notice removed Draw attention by Community Bot
Bounty Ended with no winning answer by Community Bot
edited tags
Link
Loading
deleted 162 characters in body
Source Link
Loading
Notice added Draw attention by Lucio Mazzini
Bounty Started worth 50 reputation by Lucio Mazzini
deleted 17 characters in body
Source Link
Loading
deleted 41 characters in body
Source Link
Loading
deleted 464 characters in body
Source Link
Loading
Source Link
Loading
lang-css

AltStyle によって変換されたページ (->オリジナル) /