A Wordle type game using Javascript

Wordle has been gaining quite the following over the last couple of weeks. For those not yet in the know, Wordle is a game where you have 6 tries to guess a five letter word. The word is the same for everyone and only changes once a day. If a letter within your guess is a part of the original word, the letter is highlighted orange, if it’s in the same position of the daily word then it’s green. Letters which aren’t a part of the daily word are grey. You have a choice of using a keyboard or the keyboard printed on the screen to place your letters, each of which will highlight depending on whether or not each letter is in the word.

So I decided to make a version of it myself with some differences. In this version, words will be generated randomly with each play so the game can go on for as many plays as you like throughout the day. The list of available words will also be increased to include nearly 13,000 words as an oppose to the nearly 3,000 Wordle uses, however, for the purposes of this entry and to save space, I’ll keep the word count down to just 5 but the full version can be downloaded from Github.

First we’ll set some variables in Javascript

let wordlist = ['ALPHA', 'GAMMA', 'DELTA', 'NINJA', 'SKILL'];
let currentRow = 0;
let nextRowBlock = 0;
let score = 0;
let remNotification = 0;
let gameFin = 0;
let keyPress;
let restart;
let restart2;
let enterClick;
let deleteClick;
let objArray = []
const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);

All of these are going to come into play throughout the script. countOccurences there is used to count the number of times a character shows in an array, I’ve used this to ensure the correct number of letters are highlighted grey, orange and green. The wordlist is there for the purposes of this tutorial, in the full game I’ve placed them in a separate JS.

Next, I added a container

let container = document.createElement('div');
container.id = 'container';
document.body.append(container);

added in some style for the body and container

html, body{
	margin:0 auto;
	padding:0;
	width:100%;
	height:100%;
	overflow:hidden;
	font-family: fantasy;
}

#container{
	width: 100%;
	max-width: 600px;
	margin: 0 auto;
	height: 100%;
	display: flex;
	flex-direction: column;
	border-left: 1px solid;
	border-right: 1px solid;
}

and called upon the gameStart function in Javascript we’re about to create

gameStart();

Next, we begin on the gameStart function:

function gameStart(){
	container.innerHTML = '';
	gameFin = 0;
	currentRow = 0;
	nextRowBlock = 0;
	score = 0;
	remNotification = 0;
        let wordlist = ['ALPHA', 'GAMMA', 'DELTA', 'NINJA', 'SKILL'];
	let rand = Math.floor(Math.random() * wordlist.length);
	chosenWord = wordlist[rand];

	let logo = document.createElement('div');
	logo.className = 'logo';

	let domName = 'WORDLED';
	for(i = 0; i < domName.length; i++){
		let spanClass = (i == 0 || i % 2 == 0)? 'logo_green' : 'logo_gold';
		let logoSpan = document.createElement('span');
		logoSpan.className = spanClass;
		logoSpan.innerText = domName[i];
		logo.append(logoSpan);
	}

	container.append(logo);

	let navBar = document.createElement('div');
	navBar.className = 'nav_bar';
		let giveUpBtn = document.createElement('button');
		giveUpBtn.id = 'giveUpBtn';
		giveUpBtn.innerText = 'Give up';
		giveUpBtn.addEventListener("click", function quitClick(event) {
			if(gameFin == 0){
				notification.innerText = 'The word was ' + chosenWord + '. Press Enter to play again';
				gameOver();
			}
		});
	navBar.append(giveUpBtn);
	container.append(navBar);

	let gameArea = document.createElement('div');
	gameArea.className = 'game_area';
	for(i = 0; i < 6; i++){
		let row = document.createElement('div');
		row.className = 'row';
		for(j = 0; j < 5; j++){
			let rowBlock = document.createElement('div');
			rowBlock.className = 'row_block';
			row.append(rowBlock);
		}
		gameArea.append(row);
	}
	container.append(gameArea);

	let notification = document.createElement('div');
	notification.id = 'notification';
	notification.innerText = 'Start guessing!'
	container.append(notification);

	let keyLayoutTop = 'QWERTYUIOP';
	let keyLayoutMid = 'ASDFGHJKL';
	let keyLayoutBot = 'ZXCVBNM';

	let keyboard = document.createElement('div');
	keyboard.id = 'keyboard';

		let topKeys = document.createElement('div');
		topKeys.id = 'topKeys';
		addKeys(topKeys, keyLayoutTop, 'keyboardKey_s');
		keyboard.append(topKeys);

		let midKeys = document.createElement('div');
		midKeys.id = 'midKeys';
		addKeys(midKeys, keyLayoutMid, 'keyboardKey_m');
		keyboard.append(midKeys);

		let botKeys = document.createElement('div');
		botKeys.id = 'botKeys';
		let deleteKey = document.createElement('span');
		deleteKey.className = 'keyboardKey_l';
		deleteKey.innerHTML = '&#x2190;';
		deleteKey.addEventListener("click", function deleteClick(event) {
			if(gameFin == 0){
				let wordRow = document.getElementsByClassName('row')[currentRow];
				let rowBlockEl = wordRow.childNodes;
				deleteLetter(rowBlockEl);
			}
		});
		botKeys.append(deleteKey);
		addKeys(botKeys, keyLayoutBot, 'keyboardKey_s');
		let enterKey = document.createElement('span');
		enterKey.className = 'keyboardKey_l';
		enterKey.innerText = 'Enter';
		enterKey.addEventListener("click", enterClick = function(event) {
			if(gameFin == 0){
				let wordRow = document.getElementsByClassName('row')[currentRow];
				let rowBlockEl = wordRow.childNodes;
				submitWord(wordRow);
			}
		});
		botKeys.append(enterKey);
		keyboard.append(botKeys);

	container.append(keyboard);

	let alphabet = 'abcdefghijklmnopqrstuvwxyz';
	document.addEventListener('keyup', keyPress = function(event) {
		if(gameFin == 0){
			let wordRow = document.getElementsByClassName('row')[currentRow];
			let rowBlockEl = wordRow.childNodes;
			for(i = 0; i < alphabet.length; i++){
				if ((event.key === alphabet[i] || event.key === alphabet[i].toUpperCase())) {
					addLetter(rowBlockEl, alphabet[i]);
				}
			}
			if (event.key === 'Enter') {
				submitWord(wordRow, keyPress);
			}
			if (event.key === 'Backspace') {
				deleteLetter(rowBlockEl);
			}
		}
	});
}

That’s quite a bit to take in, so let’s break down what the function is doing.

First, because the function also serves as a restart to the game, the container is emptied and the global variables are reset

container.innerHTML = '';
gameFin = 0;
currentRow = 0;
nextRowBlock = 0;
score = 0;
remNotification = 0;

We then fetch a random number between 0 and the number of words in the list and use that to fetch a word from the list

let rand = Math.floor(Math.random() * wordlist.length);
chosenWord = wordlist[rand];

Next, I placed in the logo which is basically just a word so there’s no need for images here. I set the variable domName to the word I wanted to use and placed each letter within their own span so I could style them

let logo = document.createElement('div');
logo.className = 'logo';

let domName = 'WORDLED';
for(i = 0; i < domName.length; i++){
	let spanClass = (i == 0 || i % 2 == 0)? 'logo_green' : 'logo_gold';
	let logoSpan = document.createElement('span');
	logoSpan.className = spanClass;
	logoSpan.innerText = domName[i];
	logo.append(logoSpan);
}

container.append(logo);

then I added in some style

.logo{
	text-align: center;
	margin-top: 12px;
	cursor:pointer;
	text-shadow: 2px 3px 2px #000;
	transition:.5s
}

.logo:hover{
	filter: brightness(1.125);
}

.logo_green, .logo_gold{
	font-size: 26px;
	color: #fff;
	width: 49px;
	height: 35px;
	border-radius: 12px;
	margin: 3px;
	display: inline-block;
}

.logo_gold{
	background-color: #d7bf4a;
}

.logo_green{
	background-color: #35a535;
}

Back to the JS, I then created the navBar area which currently only contains the give up button.

let navBar = document.createElement('div');
navBar.className = 'nav_bar';
	let giveUpBtn = document.createElement('button');
	giveUpBtn.id = 'giveUpBtn';
	giveUpBtn.innerText = 'Give up';
	giveUpBtn.addEventListener("click", function quitClick(event) {
		if(gameFin == 0){
			notification.innerText = 'The word was ' + chosenWord + '. Press Enter to play again';
			gameOver();
		}
	});
navBar.append(giveUpBtn);
container.append(navBar);

Here, the give up button has a click event which calls on the gameOver function and changes the notification element to display the word of the play.

Then some more styling

.nav_bar{
	text-align: center;
	height: 33px;
	margin-top: 11px;
}

#giveUpBtn{
	padding: 5px 10px;
	font-size: 17px;
	cursor: pointer;
}

Next we build the game area and the blocks/rows we’ll be guessing within. 5 blocks x 6 rows

let gameArea = document.createElement('div');
gameArea.className = 'game_area';
for(i = 0; i < 6; i++){
	let row = document.createElement('div');
	row.className = 'row';
	for(j = 0; j < 5; j++){
		let rowBlock = document.createElement('div');
		rowBlock.className = 'row_block';
		row.append(rowBlock);
	}
	gameArea.append(row);
}
container.append(gameArea);

and styled that

.game_area{
	margin-top:5px;
}

.row, .row_block{
	display: flex;
	justify-content: center;
	margin: 5px;
}

.row_block{
	font-size: 40px;
	border: 1px solid;
	width: 56px;
	height: 51px;
	border-radius: 12px;
}

Next we add in the notification element

let notification = document.createElement('div');
notification.id = 'notification';
notification.innerText = 'Start guessing!'
container.append(notification);

and its style

#notification{
	font-family: arial;
	text-align: center;
	letter-spacing: 1px;
	font-size: 19px;
	margin-top: 5px;
	margin-bottom: 5px;
	height: 22px;
}

Then we add in the on screen keyboard

let keyLayoutTop = 'QWERTYUIOP';
let keyLayoutMid = 'ASDFGHJKL';
let keyLayoutBot = 'ZXCVBNM';

let keyboard = document.createElement('div');
keyboard.id = 'keyboard';

	let topKeys = document.createElement('div');
	topKeys.id = 'topKeys';
	addKeys(topKeys, keyLayoutTop, 'keyboardKey_s');
	keyboard.append(topKeys);

	let midKeys = document.createElement('div');
	midKeys.id = 'midKeys';
	addKeys(midKeys, keyLayoutMid, 'keyboardKey_m');
	keyboard.append(midKeys);

	let botKeys = document.createElement('div');
	botKeys.id = 'botKeys';
	let deleteKey = document.createElement('span');
	deleteKey.className = 'keyboardKey_l';
	deleteKey.innerHTML = '&#x2190;';
	deleteKey.addEventListener("click", function deleteClick(event) {
		if(gameFin == 0){
			let wordRow = document.getElementsByClassName('row')[currentRow];
			let rowBlockEl = wordRow.childNodes;
			deleteLetter(rowBlockEl);
		}
	});
	botKeys.append(deleteKey);
	addKeys(botKeys, keyLayoutBot, 'keyboardKey_s');
	let enterKey = document.createElement('span');
	enterKey.className = 'keyboardKey_l';
	enterKey.innerText = 'Enter';
	enterKey.addEventListener("click", enterClick = function(event) {
		if(gameFin == 0){
			let wordRow = document.getElementsByClassName('row')[currentRow];
			let rowBlockEl = wordRow.childNodes;
			submitWord(wordRow);
		}
	});
	botKeys.append(enterKey);
	keyboard.append(botKeys);

container.append(keyboard);

Here, I separated the board into 3 different rows. I set variables for each row containing the letters in QWERTY order and inserted them using the addKeys function we will create shortly. The delete and enter keys were added in separately with each key coupled to their respective functions.

Then some more styling for the keyboard

#keyboard{
	text-align:center;
	margin-top: 5px;
}

#topKeys, #midKeys, #botKeys{
	display: flex;
	justify-content:center;
}

.keyboardKey_s, .keyboardKey_m, .keyboardKey_l{
	background-color: #c5c5c5;
	font-size: 30px;
	height: 32px;
	padding: 5px 0;
	margin: 2.5px;
	display: flex;
	justify-content: center;
	font-family: arial;
	cursor: pointer;
}

.keyboardKey_s{
	width: 52px;
}

.keyboardKey_m{
	width: 58px;
}

.keyboardKey_l{
	width: 81px;
}

The last thing to add to the stylesheet was the background colours for the tile change

.blockGreen{
	background-color: #35a535;
	color:#fff;
}

.blockGrey{
	background-color: #737373;
	color:#fff;
}

.blockGold{
	background-color: #d7bf4a;
	color:#fff;
}

We won’t need to add anything else to the stylesheet from here out. The rest is all Javascript.

Finally, back in the startGame function, I added the event listeners for keyboard use which basically runs through the alphabet, back space and enter and runs the respective function for each. Again, these functions will be created shortly.

let alphabet = 'abcdefghijklmnopqrstuvwxyz';
document.addEventListener('keyup', keyPress = function(event) {
	if(gameFin == 0){
		let wordRow = document.getElementsByClassName('row')[currentRow];
		let rowBlockEl = wordRow.childNodes;
		for(i = 0; i < alphabet.length; i++){
			if ((event.key === alphabet[i] || event.key === alphabet[i].toUpperCase())) {
				addLetter(rowBlockEl, alphabet[i]);
			}
		}
		if (event.key === 'Enter') {
			submitWord(wordRow, keyPress);
		}
		if (event.key === 'Backspace') {
			deleteLetter(rowBlockEl);
		}
	}
});

And that’s it for that function. Next we’ll add the addKeys function which checks for a click on the on screen keyboard and runs the addLetter function we’ll be creating shortly

function addKeys(el, layout, keyClass){
	for(i = 0; i < layout.length; i++){
		let j = i;
		let key = document.createElement('span');
		key.className = keyClass;
		key.id = 'keyboard_' + layout[i];
		key.innerText = layout[i];
		key.addEventListener("click", function keyboardPress(event) {
			if(gameFin == 0){
				let wordRow = document.getElementsByClassName('row')[currentRow];
				let rowBlockEl = wordRow.childNodes;
				addLetter(rowBlockEl, layout[j]);
			}
		});
		el.append(key);
	}
}

Then we add in the addLetter function which clears the notification bar and adds a letter to each block in order. After adding a letter, it increases the nextRowBlock variable by 1. When this reaches 5, it prevents you from typing any more letters

function addLetter(rowBlockEl, letter){
	if(remNotification == 0){
		remNotification = 1;
		notification.innerText = '';
	}
	if(nextRowBlock < 5){
		rowBlockEl[nextRowBlock].innerText = letter.toUpperCase();
		nextRowBlock++;
	}
}

Next we’ll add in the deleteLetter function which does exactly what it says on the tin. It deletes the last letter on the row and reduces nextRowBlock by 1

if(nextRowBlock > 0){
	nextRowBlock--;
	rowBlockEl[nextRowBlock].innerText = '';
}

Next I added in a count function which counts the number of times a character appears in a string

function count(str, find) {
    return (str.split(find)).length - 1;
}

Followed by the submitWord function

function submitWord(wordRow, keyPress){
	if(nextRowBlock > 0 && nextRowBlock % 5 == 0){
		let word = wordRow.innerText.replace(/[\n\r]/g, '');
		if(wordlist.includes(word)){
			let answer = [];
			for(i = 0; i < word.length; i++){
				let letter = word[i].toUpperCase();
				answer.push(letter);
				let blockClass = 'blockGrey';
				if(chosenWord.toUpperCase().includes(letter)){
					if(chosenWord[i].toUpperCase() === letter){
						score++;
						blockClass = ' blockGreen';
						if(count(word, letter) > count(chosenWord, letter)){
							for(j = 0; j < wordRow.childNodes.length; j++){
								if(wordRow.childNodes[j].innerText == letter && wordRow.childNodes[j].className == 'row_block  blockGold'){
									wordRow.childNodes[j].className = 'row_block  blockGrey';
									let index = answer.indexOf(letter);
									if (index !== -1) {
										answer.splice(index, 1);
									}
								}
							}
						}
					}else{
						if(count(chosenWord, letter) > 0){
							if(countOccurrences(answer, letter) <= count(chosenWord, letter)){
								blockClass = ' blockGold';
							}
							else{
								blockClass = ' blockGrey';
							}
						}
					}
				}
				wordRow.childNodes[i].className = 'row_block ' + blockClass;
				let keyboard = document.getElementById('keyboard_' + letter);
				if(chosenWord.toUpperCase().includes(letter)){
					keyboard.className += ' blockGreen';
				}
				else{
					keyboard.className += ' blockGrey';
				}
			}

			if(score === 5){
				notification.innerText = 'Well done, you won! Enter to play again';
				gameOver();
			}
			else if(currentRow == 5){
				notification.innerText = 'You lost. The word was ' + chosenWord + '. Press Enter to play again';
				gameOver();
			}
			else{
				score = 0;
				nextRowBlock = 0;
				currentRow++;
			}
		}else{
			remNotification = 0;
			notification.innerText = 'Word not in list';
		}
	}else{
		remNotification = 0;
		notification.innerText = 'You must enter 5 characters';
	}
}

Let’s break that function down to see what it does.

First we check to see if the row contains 5 letters by checking the nextRowBlock variable

if(nextRowBlock > 0 && nextRowBlock % 5 == 0){

I then took the innerText of the wordRow element which is set on each call and removed all new lines

let word = wordRow.innerText.replace(/[\n\r]/g, '');

I then checked to see if the word list contains the guess, ensuring the guess isn’t just random letters typed in

if(wordlist.includes(word)){

next we set an empty array variable to dump in each letter

let answer = [];

followed by running through the guess letter by letter

for(i = 0; i < word.length; i++){

set a variable to contain the letter in uppercase so I didn’t have to keep on typing word[i].toUpperCase()

let letter = word[i].toUpperCase();

pushed each letter to the array

answer.push(letter);

and set a variable for the blockClass which is going to change the background colour of the row block depending on whether or not the letter is in the word and in the correct place

let blockClass = 'blockGrey';

Next I checked to see if the chosen word contains the letter

if(chosenWord.toUpperCase().includes(letter)){

then checked to see if the current letter is in the same position as the chosen word

if(chosenWord[i].toUpperCase() === letter){

if the letters are in equal positions, I increased the score by 1 and set blockClass to green, then checked to see if the guess contained more of the same letter than the chosen word

score++;
blockClass = ' blockGreen';
if(count(word, letter) > count(chosenWord, letter)){

If the guess does contain more of the same letter, it iterates over the row’s child elements, checks for all containing the same letter with the gold class, changes it to grey and removes the letter from the array we set earlier

for(j = 0; j < wordRow.childNodes.length; j++){
	if(wordRow.childNodes[j].innerText == letter && wordRow.childNodes[j].className == 'row_block  blockGold'){
		wordRow.childNodes[j].className = 'row_block  blockGrey';
		let index = answer.indexOf(letter);
		if (index !== -1) {
			answer.splice(index, 1);
		}
	}
}

Next, if the current letter isn’t in the same position as the letter in the chosen word, we count the number of times the letter has already appeared from the guess and if it’s lower than or equal to the number of times it’s in the chosen word, we highlight it gold, else we highlight it grey

else{
	if(countOccurrences(answer, letter) <= count(chosenWord, letter)){
		blockClass = ' blockGold';
	}
	else{
		blockClass = ' blockGrey';
	}
}

Then we update the classes of the blocks in the row

wordRow.childNodes[i].className = 'row_block ' + blockClass;

Next, we want to highlight the keyboard on the screen to let the player know which letters are and aren’t a part of the word. First we set the element to a variable

let keyboard = document.getElementById('keyboard_' + letter);

Then we check to see if the letter of the board is in the word. If it is we’ll add a green class, else we add in a grey

if(chosenWord.toUpperCase().includes(letter)){
	keyboard.className += ' blockGreen';
}
else{
	keyboard.className += ' blockGrey';
}

Next we check out the score. If it equals 5, the player has guessed the word, the notification gets set with a congratulations and gameOver is called

if(score === 5){
	notification.innerText = 'Well done, you won! Enter to play again';
	gameOver();
}

else if the player is on the last row, the player has lost, the notification set and game over is called

else if(currentRow == 5){
	notification.innerText = 'You lost. The word was ' + chosenWord + '. Press Enter to play again';
	gameOver();
}

else the score is reset and the player moves onto the next try

else{
	score = 0;
	nextRowBlock = 0;
	currentRow++;
}

The next else statement is for if the word isn’t in the word list, in which case remNotification is set to 0 and the notification lets the player know the word doesn’t exist

else{
	remNotification = 0;
	notification.innerText = 'Word not in list';
}

and the final else statement is in case the player didn’t enter 5 characters, in which case a warning pops up in the notifications

else{
	remNotification = 0;
	notification.innerText = 'You must enter 5 characters';
}

Finally we add in the gameOver function which removes all event listeners, then changes the listener for the Enter key to start the game again

function gameOver(){
	gameFin = 1;
	document.removeEventListener('keyup', deleteClick, false);
	document.removeEventListener('keyup', enterClick, false);
	document.removeEventListener('keyup', keyPress, false);
	document.removeEventListener('keyup', restart, false);
	document.addEventListener('keyup', restart = function(event) {
		if (event.key === 'Enter') {
			document.removeEventListener('keyup', restart, false);
			gameStart();
		}
	});
}

And that’s it. You should have a functioning Wordle based game that you can play whenever you like and something like this:

The full game can be downloaded from Github here or it can be played on codePen here (You may want to change the view to play it properly)

The full code:

Javascript

let wordlist = ['ALPHA', 'GAMMA', 'DELTA', 'NINJA', 'SKILL'];
let currentRow = 0;
let nextRowBlock = 0;
let score = 0;
let remNotification = 0;
let gameFin = 0;
let keyPress;
let restart;
let restart2;
let enterClick;
let deleteClick;
let objArray = []
const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);

let container = document.createElement('div');
container.id = 'container';
document.body.append(container);

gameStart();

function gameOver(){
	gameFin = 1;
	document.removeEventListener('keyup', deleteClick, false);
	document.removeEventListener('keyup', enterClick, false);
	document.removeEventListener('keyup', keyPress, false);
	document.removeEventListener('keyup', restart, false);
	document.addEventListener('keyup', restart = function(event) {
		if (event.key === 'Enter') {
			document.removeEventListener('keyup', restart, false);
			gameStart();
		}
	});
}

function gameStart(){
	container.innerHTML = '';
	gameFin = 0;
	currentRow = 0;
	nextRowBlock = 0;
	score = 0;
	remNotification = 0;
	let rand = Math.floor(Math.random() * wordlist.length);
	chosenWord = wordlist[rand];

	let logo = document.createElement('div');
	logo.className = 'logo';

	let domName = 'WORDLED';
	for(i = 0; i < domName.length; i++){
		let spanClass = (i == 0 || i % 2 == 0)? 'logo_green' : 'logo_gold';
		let logoSpan = document.createElement('span');
		logoSpan.className = spanClass;
		logoSpan.innerText = domName[i];
		logo.append(logoSpan);
	}

	container.append(logo);

	let navBar = document.createElement('div');
	navBar.className = 'nav_bar';
		let giveUpBtn = document.createElement('button');
		giveUpBtn.id = 'giveUpBtn';
		giveUpBtn.innerText = 'Give up';
		giveUpBtn.addEventListener("click", function quitClick(event) {
			if(gameFin == 0){
				notification.innerText = 'The word was ' + chosenWord + '. Press Enter to play again';
				gameOver();
			}
		});
	navBar.append(giveUpBtn);
	container.append(navBar);

	let gameArea = document.createElement('div');
	gameArea.className = 'game_area';
	for(i = 0; i < 6; i++){
		let row = document.createElement('div');
		row.className = 'row';
		for(j = 0; j < 5; j++){
			let rowBlock = document.createElement('div');
			rowBlock.className = 'row_block';
			row.append(rowBlock);
		}
		gameArea.append(row);
	}
	container.append(gameArea);

	let notification = document.createElement('div');
	notification.id = 'notification';
	notification.innerText = 'Start guessing!'
	container.append(notification);

	let keyLayoutTop = 'QWERTYUIOP';
	let keyLayoutMid = 'ASDFGHJKL';
	let keyLayoutBot = 'ZXCVBNM';

	let keyboard = document.createElement('div');
	keyboard.id = 'keyboard';

		let topKeys = document.createElement('div');
		topKeys.id = 'topKeys';
		addKeys(topKeys, keyLayoutTop, 'keyboardKey_s');
		keyboard.append(topKeys);

		let midKeys = document.createElement('div');
		midKeys.id = 'midKeys';
		addKeys(midKeys, keyLayoutMid, 'keyboardKey_m');
		keyboard.append(midKeys);

		let botKeys = document.createElement('div');
		botKeys.id = 'botKeys';
		let deleteKey = document.createElement('span');
		deleteKey.className = 'keyboardKey_l';
		deleteKey.innerHTML = '&#x2190;';
		deleteKey.addEventListener("click", function deleteClick(event) {
			if(gameFin == 0){
				let wordRow = document.getElementsByClassName('row')[currentRow];
				let rowBlockEl = wordRow.childNodes;
				deleteLetter(rowBlockEl);
			}
		});
		botKeys.append(deleteKey);
		addKeys(botKeys, keyLayoutBot, 'keyboardKey_s');
		let enterKey = document.createElement('span');
		enterKey.className = 'keyboardKey_l';
		enterKey.innerText = 'Enter';
		enterKey.addEventListener("click", enterClick = function(event) {
			if(gameFin == 0){
				let wordRow = document.getElementsByClassName('row')[currentRow];
				let rowBlockEl = wordRow.childNodes;
				submitWord(wordRow);
			}
		});
		botKeys.append(enterKey);
		keyboard.append(botKeys);

	container.append(keyboard);

	let alphabet = 'abcdefghijklmnopqrstuvwxyz';
	document.addEventListener('keyup', keyPress = function(event) {
		if(gameFin == 0){
			let wordRow = document.getElementsByClassName('row')[currentRow];
			let rowBlockEl = wordRow.childNodes;
			for(i = 0; i < alphabet.length; i++){
				if ((event.key === alphabet[i] || event.key === alphabet[i].toUpperCase())) {
					addLetter(rowBlockEl, alphabet[i]);
				}
			}
			if (event.key === 'Enter') {
				submitWord(wordRow, keyPress);
			}
			if (event.key === 'Backspace') {
				deleteLetter(rowBlockEl);
			}
		}
	});
}

function deleteLetter(rowBlockEl){
	if(nextRowBlock > 0){
		nextRowBlock--;
		rowBlockEl[nextRowBlock].innerText = '';
	}
}

function count(str, find) {
    return (str.split(find)).length - 1;
}

function submitWord(wordRow, keyPress){
	if(nextRowBlock > 0 && nextRowBlock % 5 == 0){
		let word = wordRow.innerText.replace(/[\n\r]/g, '');
		if(wordlist.includes(word)){
			let answer = [];
			for(i = 0; i < word.length; i++){
				let letter = word[i].toUpperCase();
				answer.push(letter);
				let blockClass = 'blockGrey';
				if(chosenWord.toUpperCase().includes(letter)){
					if(chosenWord[i].toUpperCase() === letter){
						score++;
						blockClass = ' blockGreen';
						if(count(word, letter) > count(chosenWord, letter)){
							for(j = 0; j < wordRow.childNodes.length; j++){
								if(wordRow.childNodes[j].innerText == letter && wordRow.childNodes[j].className == 'row_block  blockGold'){
									wordRow.childNodes[j].className = 'row_block  blockGrey';
									let index = answer.indexOf(letter);
									if (index !== -1) {
										answer.splice(index, 1);
									}
								}
							}
						}
					}else{
						if(countOccurrences(answer, letter) <= count(chosenWord, letter)){
							blockClass = ' blockGold';
						}
						else{
							blockClass = ' blockGrey';
						}
					}
				}
				wordRow.childNodes[i].className = 'row_block ' + blockClass;
				let keyboard = document.getElementById('keyboard_' + letter);
				if(chosenWord.toUpperCase().includes(letter)){
					keyboard.className += ' blockGreen';
				}
				else{
					keyboard.className += ' blockGrey';
				}
			}

			if(score === 5){
				notification.innerText = 'Well done, you won! Enter to play again';
				gameOver();
			}
			else if(currentRow == 5){
				notification.innerText = 'You lost. The word was ' + chosenWord + '. Press Enter to play again';
				gameOver();
			}
			else{
				score = 0;
				nextRowBlock = 0;
				currentRow++;
			}
		}else{
			remNotification = 0;
			notification.innerText = 'Word not in list';
		}
	}else{
		remNotification = 0;
		notification.innerText = 'You must enter 5 characters';
	}
}

function addKeys(el, layout, keyClass){
	for(i = 0; i < layout.length; i++){
		let j = i;
		let key = document.createElement('span');
		key.className = keyClass;
		key.id = 'keyboard_' + layout[i];
		key.innerText = layout[i];
		key.addEventListener("click", function keyboardPress(event) {
			if(gameFin == 0){
				let wordRow = document.getElementsByClassName('row')[currentRow];
				let rowBlockEl = wordRow.childNodes;
				addLetter(rowBlockEl, layout[j]);
			}
		});
		el.append(key);
	}
}

function addLetter(rowBlockEl, letter){
	if(remNotification == 0){
		remNotification = 1;
		notification.innerText = '';
	}
	if(nextRowBlock < 5){
		rowBlockEl[nextRowBlock].innerText = letter.toUpperCase();
		nextRowBlock++;
	}
}

CSS

html, body{
	margin:0 auto;
	padding:0;
	width:100%;
	height:100%;
	overflow:hidden;
	font-family: fantasy;
}

#container{
	width: 100%;
	max-width: 600px;
	margin: 0 auto;
	height: 100%;
	display: flex;
	flex-direction: column;
	border-left: 1px solid;
	border-right: 1px solid;
}

.logo{
	text-align: center;
	margin-top: 12px;
	cursor:pointer;
	text-shadow: 2px 3px 2px #000;
	transition:.5s
}

.logo:hover{
	filter: brightness(1.125);
}

.logo_green, .logo_gold{
	font-size: 26px;
	color: #fff;
	width: 49px;
	height: 35px;
	border-radius: 12px;
	margin: 3px;
	display: inline-block;
}

.logo_gold{
	background-color: #d7bf4a;
}

.logo_green{
	background-color: #35a535;
}

.nav_bar{
	text-align: center;
	height: 33px;
	margin-top: 11px;
}

#giveUpBtn{
	padding: 5px 10px;
	font-size: 17px;
	cursor: pointer;
}

.game_area{
	margin-top:5px;
}

.row, .row_block{
	display: flex;
	justify-content: center;
	margin: 5px;
}

.row_block{
	font-size: 40px;
	border: 1px solid;
	width: 56px;
	height: 51px;
	border-radius: 12px;
}

#notification{
	font-family: arial;
	text-align: center;
	letter-spacing: 1px;
	font-size: 19px;
	margin-top: 5px;
	margin-bottom: 5px;
	height: 22px;
}

#keyboard{
	text-align:center;
	margin-top: 5px;
}

#topKeys, #midKeys, #botKeys{
	display: flex;
	justify-content:center;
}

.keyboardKey_s, .keyboardKey_m, .keyboardKey_l{
	background-color: #c5c5c5;
	font-size: 30px;
	height: 32px;
	padding: 5px 0;
	margin: 2.5px;
	display: flex;
	justify-content: center;
	font-family: arial;
	cursor: pointer;
}

.keyboardKey_s{
	width: 52px;
}

.keyboardKey_m{
	width: 58px;
}

.keyboardKey_l{
	width: 81px;
}


.blockGreen{
	background-color: #35a535;
	color:#fff;
}

.blockGrey{
	background-color: #737373;
	color:#fff;
}

.blockGold{
	background-color: #d7bf4a;
	color:#fff;
}
1 Like

Very cool!

I’m definitely a Wordler (yesterday’s word was pretty dang tricky), so this is fun to see!

What a great project. Lots of fun to play and helpful in learning Javascript.

I’m planning on using it with grandkids as a fun way to improve their vocabulary. To do this I needed to create a shorter answer list, but use the longer wordlist to enable wilder guesses. It only required changing two lines of code, and adding a new “answers” array.

let rand = Math.floor(Math.random() * answers.length);
chosenWord = answers[rand];

I forked the repo and submitted a pull request.

1 Like