Video Tutorial:
If you are interested to learn by watching a video tutorial rather than reading this block post, you can check out the video below. Also, subscribe to my YouTube channel where I post new tutorials tricks and tips every alternate day.
Project Folder Structure:
Let’s first create the project folder structure. We create a project folder called wordle. Inside this folder, we have three files these files index.html, style.css, and script.js. The first file is the HTML document. While the second one is the stylesheet. We finally have the script file.
HTML:
The HTML file consists of elements that build the layout of our app. Copy the code below and paste it into your HTML document.
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Wordle</title> <!-- Google Font --> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@600&display=swap" rel="stylesheet" /> <!-- Stylesheet --> <link rel="stylesheet" href="style.css" /> </head> <body> <div class="wrapper"> <div class="container"></div> <div class="rules"> <img src="rules.svg" /> </div> <div class="win-screen hide"></div> </div> <button class="submit hide">Submit</button> <!-- Script --> <script src="script.js"></script> </body> </html>
CSS:
Next, we style the game using CSS. For this copy, the code provided to you below and paste it into your style sheet.
* { padding: 0; margin: 0; box-sizing: border-box; font-family: "Poppins", sans-serif; } body { height: 100vh; background: linear-gradient(135deg, #c59cff, #7e5bfb); } .wrapper { position: absolute; transform: translate(-50%, -50%); top: 50%; left: 50%; display: flex; gap: 1em; background-color: #ffffff; padding: 1em; border-radius: 0.5em; box-shadow: 0 2.5em 3.75em rgba(30, 19, 70, 0.3); } .container, .rules { width: 18.75em; height: 22.5em; position: relative; } .input-group { display: grid; width: 100%; grid-template-columns: auto auto auto auto auto; column-gap: 0.2em; margin: 0.2em 0; } .input-box { font-size: 2em; width: 1.7em; height: 1.7em; text-align: center; display: block; border: 1px solid #000000; line-height: 1; font-weight: 600; text-transform: uppercase; } .input-box:disabled { color: #000000; } .correct { background-color: #6aaa64; } .exists { background-color: #c9b458; } .incorrect { background-color: #787c7e; } .correct, .exists, .incorrect { color: #ffffff; } .win-screen { font-weight: 400; position: absolute; background-color: #ffffff; height: 100%; width: 100%; top: 0; left: 0; border-radius: 0.5em; display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 1em; } .win-screen button { font-size: 1.2em; background-color: #7e5bfb; border: none; outline: none; font-weight: 400; padding: 0.8em 2em; border-radius: 2em; } .hide { display: none; } @media screen and (max-width: 700px) { .wrapper { flex-direction: column; transform: translateX(-50%); left: 50%; top: 0.5em; } }
Javascript:
We finally had functionality to the game with JavaScript we do this in the following steps:
Create initial references
Create an initial setup
A function to generate a random word
A function to update input to disabled status and set focus
The logic for writing in inputs
Comparison logic
let words = [ "Zebra", "Sling", "Crate", "Brick", "press", "truth", "sweet", "salty", "alert", "check", "roast", "toast", "shred", "cheek", "shock", "czech", "woman", "wreck", "court", "coast", "flake", "think", "smoke", "unrig", "slant", "ultra", "vague", "pouch", "radix", "yeast", "zoned", "cause", "quick", "bloat", "level", "civil", "civic", "madam", "house", "delay", ]; let container = document.querySelector(".container"); let winScreen = document.querySelector(".win-screen"); let submitButton = document.querySelector(".submit"); let inputCount, tryCount, inputRow; let backSpaceCount = 0; let randomWord, finalWord; //Detect touch device const isTouchDevice = () => { try { //We try to create TouchEvent (it would fail for desktops and throw error) document.createEvent("TouchEvent"); return true; } catch (e) { return false; } }; //Initial Setup const startGame = async () => { winScreen.classList.add("hide"); container.innerHTML = ""; inputCount = 0; successCount = 0; tryCount = 0; finalWord = ""; //Creating the grid for (let i = 0; i < 6; i++) { let inputGroup = document.createElement("div"); inputGroup.classList.add("input-group"); for (let j = 0; j < 5; j++) { //Disabled by default. We will enable one by one inputGroup.innerHTML += `<input type="text" class="input-box" onkeyup="checker(event)" maxlength="1" disabled>`; } await container.appendChild(inputGroup); } inputRow = document.querySelectorAll(".input-group"); inputBox = document.querySelectorAll(".input-box"); updateDivConfig(inputRow[tryCount].firstChild, false); randomWord = getRandom(); console.log(randomWord); }; //Get random word const getRandom = () => words[Math.floor(Math.random() * words.length)].toUpperCase(); //Update input to disabled status and set focus const updateDivConfig = (element, disabledStatus) => { element.disabled = disabledStatus; if (!disabledStatus) { element.focus(); } }; //Logic for writing in the inputs const checker = async (e) => { let value = e.target.value.toUpperCase(); //disable current input box updateDivConfig(e.target, true); if (value.length == 1) { //if the word is lesss than 5 length and the button isn't backspace if (inputCount <= 4 && e.key != "Backspace") { //Attach the letter to the final word finalWord += value; if (inputCount < 4) { //enable next updateDivConfig(e.target.nextSibling, false); } } inputCount += 1; } else if (value.length == 0 && e.key == "Backspace") { //Empty input box anduser press Backspace finalWord = finalWord.substring(0, finalWord.length - 1); if (inputCount == 0) { //For first inputbox updateDivConfig(e.target, false); return false; } updateDivConfig(e.target, true); e.target.previousSibling.value = ""; //enable previous and decrement count updateDivConfig(e.target.previousSibling, false); inputCount = -1; } }; //When user presses enter/backspace and all the inputs are filled window.addEventListener("keyup", (e) => { if (inputCount > 4) { if (isTouchDevice()) { submitButton.classList.remove("hide"); } if (e.key == "Enter") { validateWord(); } else if (e.key == "Backspace") { inputRow[tryCount].lastChild.value = ""; finalWord = finalWord.substring(0, finalWord.length - 1); updateDivConfig(inputRow[tryCount].lastChild, false); inputCount -= 1; } } }); //Comparison Logic const validateWord = async () => { if (isTouchDevice()) { submitButton.classList.add("hide"); } let failed = false; //Get all input boxes of current row let currentInputs = inputRow[tryCount].querySelectorAll(".input-box"); //Check if it is a valid english word await fetch( `https://api.dictionaryapi.dev/api/v2/entries/en/${finalWord}` ).then((response) => { if (response.status == "404") { console.clear(); alert("Please Enter Valid Word"); failed = true; } }); //If not then stop here if (failed) { return false; } //Initially set these let successCount = 0; let successLetters = ""; //Checks for both words for (let i in randomWord) { //if same then green if (finalWord[i] == randomWord[i]) { currentInputs[i].classList.add("correct"); successCount += 1; successLetters += randomWord[i]; } else if ( randomWord.includes(finalWord[i]) && !successLetters.includes(finalWord[i]) ) { //If the letter exist in the chosen word and is not present in the success array then yellow currentInputs[i].classList.add("exists"); } else { currentInputs[i].classList.add("incorrect"); } } //Increment try count tryCount += 1; //If all letters are correct if (successCount == 5) { //Display the win banner after 1 second setTimeout(() => { winScreen.classList.remove("hide"); winScreen.innerHTML = ` <span>Total guesses: ${tryCount}</span> <button onclick="startGame()">New Game</button> `; }, 1000); } else { //unsuccessful so next attempt inputCount = 0; finalWord = ""; if (tryCount == 6) { //all attempts wrong tryCount = 0; winScreen.classList.remove("hide"); winScreen.innerHTML = ` <span>You lose</span> <button onclick="startGame()">New Game</button>`; return false; } //for next attempt move to first child of next row updateDivConfig(inputRow[tryCount].firstChild, false); } inputCount = 0; }; window.onload = startGame();
That’s all for this tutorial. If you face any issues while creating this project you can download the source code by clicking on the download button below also so if you have any queries suggestions or feedback you can comment below.
Happy coding!
Hello, I absolutely love your works!
Might be wrong but it seems that correct but misplaced chars aren’t counted. Leading in a wrong result if the randomWord is “Cheeks” and the finalWord is “Eerie”. All 3 “e” will be yellow.
I played around with that Wordle game mechanism but on Swift 🙂
Hi,
Thanks for a great tutorial – I learned lots!
But I think the js script has an error on line 127:
inputCount = -1;
It should be:
inputCount -= 1;