Building a JavaScript Matching Game

One of the first applications I built when l started learning JavaScript was a simple matching game where the player has to find all the matching tiles to win. The game includes a timer and player rating. This is a great project to start off with because it makes substantial use of event listeners, different object types, and custom components. In this article, I’ll walk through my approach.

Before coding anything, it always helps me to map out what I want the app to do.

I want to show the following components:
1. Game title
2. Timer: In the format 0:00 and starts when the first card is flipped and doesn’t stop until the game is won or the refresh icon is clicked.
3. Player rating: A series of four stars that decrements after a set number of moves.
4. Refresh icon: When the icon is clicked, the game board is reset, the timer is stopped and goes back to 0:00, and the player rating is replenished to four stars.
5. Game board: A container for holding the game tiles
6. Game tiles: A set of sixteen tiles arranged in four stacked rows. The tiles should all show the same image on their backs, which is what is shown when gameplay begins. The front of the cards, shown when clicked, should be eight pairs of images to be matched during gameplay.
7. A modal to show game stats when the game is won.

And the flow of gameplay should be:
1. The user begins play by clicking on a tile, which starts on the game timer and the rating system.
2. The user clicks the next tile and either (1) the tiles are a match and they stay flipped OR (2) the tiles are not a match and they both flip back over.
3. Gameplay continues that way until all cards are matched, at which point, the user is shown a modal with their statistics (time taken and rating) and the option to exit or play again.

The first thing I did was set up an initial static playing board in an HTML file. This was basically a div to hold all the game tiles. I then used JavaScript to create the card objects (scenes) and append them to the DOM.

createCardScenes and loadGame functions that set the game board

It’s up to you whether you want to add most of the code for building the DOM in your HTML file or in your JS file. I prefer to do it in JS because I can have more of my component in one file. It saves me from having to reference a class or id defined in my HTML file when adding an event listener to a board component. For example, rather than doing something like this:

document.getElementById(‘id_of_button’).addEventListener(‘click’, ( ) =>{ functionToExecute( ) }

I can just apply the event listener at the moment I create the element:

document.createElement(‘button’).addEventListener(‘click’, ( ) =>{ functionToExecute( ) }

For example, below is how I built each card through a createCard function, starting with creating the card object, then appending the front, back, and the event listener to execute a flip.

I built out the rest of the game board the same way.

To execute the card flip, I set the card front and back into a card div and then set that card div into a “card scene”. I want the card to flip when the player clicks on it, so I add an event listener that makes the card div rotate 180 degrees and sets the class to “active” so it can be compared against the second flipped card.

Seems fair enough, but what about clicking the card again? Wouldn’t it just flip back over without the player being able to check the match? I want to take that possibility out of the equation, so after a card is clicked, I add a line to the flipCard function to remove the click event listener. This solves the problem of the card being clicked twice by mistake.

For the timer, I envisioned a three-digit format of 0:00 (going to four digits after gameplay reaches ten minutes), which would start only after the first card is flipped. So, part of the executeCardFlip function is a callback function to start the clock — startTimer.

The startTimer function sets the boolean value of the timerActive variable to true, which in turn allows the runTimer function to fire every second. This increments the timer in the DOM until the game has been refreshed or won.

One issue that I ran into building the game was how to count moves. The first question I had to answer was whether to count each flip as a move or only count when a match was attempted on the second flip. I landed on the latter but then that led to the question of how to code for only counting moves on the second flip out of two. I decided to set another variable to hold intermediate moves. A game move would only be counted when intermediate moves reached 2, then intermediate moves would be reset to zero. I implemented this in the handleMove function.

Incrementing intermediate moves and total moves (if there are 2 intermediate moves) in the handleMove function, which is executed each time a tile is clicked

Ratings in the game are based on how many moves a player has made, so each time the global moves variable is incremented, I wanted a function that would make any necessary adjustments to the player’s rating. For this purpose, I use the checkAndAdjustRating function, which checks total moves and removes stars from the rating element if the number of moves reaches a certain amount. As a side note, I’m able to rely on .querySelector here to remove the stars because they all share the same class and .querySelector only returns the first element in the class.

Once the player achieves all the matches, the game board will gray out/deactivate and a game over modal will be displayed with game stats. The stats are saved in variables so it’s just a matter of pulling the values for the number of moves, time elapsed, and rating, then populating the values to the modal.

Thanks for reading! As an additional feature, I may add a timed version where the game will end after a certain amount of time has passed, even if all the matches haven’t been found yet. Another adjustment I might make to the code is to move some of the styling out of the JS file and into Sass/CSS. This would make the functions more modular and clean up the JS code. I did a lot in the JS file because of personal preference and I also wanted to practice coding with JavaScript. :) You can play the game here. Also, feel free to check out my Github for the full source code.

Web developer and lawyer