Memory Game, also known as Matching Game, is a simple card game where you need to match pairs by turning over 2 cards at a time. In this workshop, we are going to build a simple memory game using JavaScript. Take a peek at the final project and the code.
Live Demo and Source Code.
How to play...
Rules
- You will start by flipping over one card
- If the next card you flip matches, you get +1 to your score
- These cards then disappear
- If the next card you flip does not match, the cards flip back
- The game continues until you match all the cards on the board
Prerequisites
Basic knowledge of HTML5, CSS3, and JavaScript. We will use some in-built functions of JavaScript. Also, the styling will be as simple as possible.
Setup
Repl.it is an online code editor. There is no need to install anything which makes coding simpler.
Fork the starter repl here. Your development environment will be ready in a few seconds!
It contains an empty index.html
file linked with style.css
and script.js
files.
We will create a simple 3 x 4 memory card game in this workshop. We will use images for the cards. I will provide the public links for the images along with the code. If you prefer to download, you can download them here. You can also add your own images. Make sure that they are 100 x 100 px to avoid further styling.
After setting up let's get going.
The HTML
Let's create the markup of our game.
Change the title and create a heading in the body. Now create a paragraph with a span of id score
.
<h1>~ Memory Game ~</h1>
<p>Score:<span id="score"></span></p>
Create a div
element with a class of board
. We will create the game board using JavaScript inside this div
.
<div class="board"></div>
Your final index.html will look something like this.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Memory Game</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>~ Memory Game ~</h1>
<p>Score:<span id="score"></span></p>
<div class="board"></div>
<script src="script.js"></script>
</body>
</html>
Click the Run button to check your output, whether all the tags are working.
All the styles are prewritten in the starter template. You will see this :point_up_2: kind of output.
We completed the markup of our project. Now flip over to script.js
and let's start creating the game.
The JavaScript
Create a DOM event-listener for the event DOMContentLoaded. All our JavaScript code will be inside the event listener, which will be executed after the content of the web page is loaded.
document.addEventListener('DOMContentLoaded', () => {
// code goes here!
})
Inside the event-listener, create an array for the cards which we use for the game. Add two of each card for matching the cards. The public links for the images are provided in the code below. If you downloaded the images or using your own images, then add the relative path to the images. Also, name them as you wish.
document.addEventListener('DOMContentLoaded', () => {
const cardArray = [
{
name: '1',
img: 'https://cloud-5ystxzer7.vercel.app/11.png'
},
{
name: '2',
img: 'https://cloud-5ystxzer7.vercel.app/22.png'
},
{
name: '3',
img: 'https://cloud-5ystxzer7.vercel.app/33.png'
},
{
name: '4',
img: 'https://cloud-5ystxzer7.vercel.app/44.png'
},
{
name: '5',
img: 'https://cloud-5ystxzer7.vercel.app/55.png'
},
{
name: '6',
img: 'https://cloud-5ystxzer7.vercel.app/06.png'
},
{
name: '1',
img: 'https://cloud-5ystxzer7.vercel.app/11.png'
},
{
name: '2',
img: 'https://cloud-5ystxzer7.vercel.app/22.png'
},
{
name: '3',
img: 'https://cloud-5ystxzer7.vercel.app/33.png'
},
{
name: '4',
img: 'https://cloud-5ystxzer7.vercel.app/44.png'
},
{
name: '5',
img: 'https://cloud-5ystxzer7.vercel.app/55.png'
},
{
name: '6',
img: 'https://cloud-5ystxzer7.vercel.app/06.png'
}
]
// All the code goes here
})
Now we are good to create our game board.
Game Board
Remember, we are going code everything after the cardArray
.
Now we have to create four constants as:
board
: thediv
element with class ofboard
using querySelector.result
: thespan
with an id ofscore
, to add the live score.placeholder
: for placeholder image. Placeholder image represents the backside of our cards.blank
: for the blank image. In place of the empty card, a blank image is displayed.
const board = document.querySelector('.board')
const result = document.querySelector('#score')
const placeholder = 'https://cloud-5ystxzer7.vercel.app/7placeholder.png'
const blank = 'https://cloud-5ystxzer7.vercel.app/6blank.png'
Now create a function createBoard()
and loop over through elements in cardArray
using for
loop and add cards to our game board.
function createBoard() {
for (let i = 0; i < cardArray.length; i++) {
// code goes here
}
}
Create a img
element using document.createElement
for the every card.
var card = document.createElement('img')
Using setAttribute()
, add src
and data-id
attributes to the image. Let's first set the src
attribute to the placeholder image (i.e, let's assume the placeholder image as a reversed card). The link to the placeholder image is given in the code, otherwise, add the relative path to the image.
card.setAttribute('src', placeholder)
card.setAttribute('data-id', i)
Explanation: Here,
- createElement() creates the HTML element specified by tagName
- setAttribute() sets the value of an attribute on the specified element
- data attribute allow us to store extra information on standard in the HTML tag.
According to the game, we have to flip the card that is clicked. Add a click
event-listener for the card. Every time a card gets clicked, flipCard
function will be executed, which we will define later on the flow. For now, comment the event-listener as flipCard
haven't yet defined.
card.addEventListener('click', flipCard)
Then add the card
into the board
using appendChild()
.
board.appendChild(card)
The method appendChild() adds a node to the end of the list of children of a specified parent node.
The function we created finally, will be:
const board = document.querySelector('.board')
const placeholder = 'https://cloud-5ystxzer7.vercel.app/7placeholder.png'
// create game board
function createBoard() {
for (let i = 0; i < cardArray.length; i++) {
var card = document.createElement('img')
card.setAttribute('src', placeholder)
card.setAttribute('data-id', i)
card.addEventListener('click', flipCard)
board.appendChild(card)
}
}
createBoard()
Press the run button and see your game board.
It looks like this with all the cards turned down (i.e., placeholder images).
Flip Card
When a card is clicked, let's flip the card.
Create two empty variable arrays cardsClicked
and cardsClickedId
. Also, create a variable cardsMatched
array to push the matched cards
var cardsClicked = []
var cardsClickedId = []
var cardsMatched = []
Create a function flipCard()
. Then inside this function, create a variable cardId
, which is the data-id
attribute of the clicked card, using getAttribute()
.
function flipCard() {
var cardId = this.getAttribute('data-id')
// code goes here
}
Now add name of this card into cardsClicked
array based on cardId
using push()
method. Also push the id of this card (i.e., cardId
) into cardsClickedId
array.
cardsClicked.push(cardArray[cardId].name)
cardsClickedId.push(cardId)
Then add the front side of the card, the image in cardArray
corresponding to cardId
, using setAttribute
.
this.setAttribute('src', cardArray[cardId].img)
After selecting two cards, we have to check for a match. For that, if two cards are clicked i.e., the length of cardsClicked
array is equal to 2
, invoke checkForMatch()
inside setTimeout()
so that everything doesn't happen too fast.
if (cardsClicked.length === 2) {
setTimeout(checkForMatch, 500)
}
Explanation: Here,
- getAttribute() returns the value of a specified attribute on the element.
- push() method adds one or more elements to the end of an array and returns the new length of the array.
- setTimeout() sets a timer which executes a function or specified piece of code once the timer expires.
Final flipCard
function will be:
function flipCard() {
var cardId = this.getAttribute('data-id')
cardsClicked.push(cardArray[cardId].name)
cardsClickedId.push(cardId)
this.setAttribute('src', cardArray[cardId].img)
if (cardsClicked.length === 2) {
setTimeout(checkForMatch, 500)
}
}
Here's what the code looks like so far:
document.addEventListener('DOMContentLoaded', () => {
const cardArray = [....]// the cardArray we created before
const board = document.querySelector('.board')
const result = document.querySelector('#score')
const placeholder = "https://cloud-5ystxzer7.vercel.app/7placeholder.png"
const blank = "https://cloud-5ystxzer7.vercel.app/6blank.png"
var cardsClicked = []
var cardsClickedId = []
var cardsMatched = []
//creating game board
function createBoard() {
for (let i = 0; i < cardArray.length; i++) {
var card = document.createElement('img')
card.setAttribute('src', placeholder)
card.setAttribute('data-id', i)
card.addEventListener('click', flipCard)
board.appendChild(card)
}
}
//flip the card
function flipCard() {
var cardId = this.getAttribute('data-id')
cardsClicked.push(cardArray[cardId].name)
cardsClickedId.push(cardId)
this.setAttribute('src', cardArray[cardId].img)
if (cardsClicked.length === 2) {
setTimeout(checkForMatch, 500)
}
}
createBoard()
})
Don't forget to uncomment the event-listener of the card.
Comment the if
statement in flipCard
function and check whether the images are changing or not. The output works like this.
We created a flippable game board.
Now that we have flipping cards, let’s handle the matching logic.
Match Card
When we click the first card, it needs to wait until another card is flipped. So now, when the user clicks the second card, we will check to see if it’s a match.
In order to do that, let’s create a function checkForMatch()
. Inside the function, select all the images we created using querySelectorAll()
and define a cards
array. Also get the two elements in cardsClickedId
array into two variables firstCard
and secondCard
respectively.
function checkForMatch() {
var cards = document.querySelectorAll('img')
const firstCard = cardsClickedId[0]
const secondCard = cardsClickedId[1]
// upcoming code
}
If you click the same card again, a pop-up alert notifies you and the cards flip back.
if (firstCard === secondCard) {
cards[firstCard].setAttribute('src', placeholder)
cards[secondCard].setAttribute('src', placeholder)
alert('You have clicked the same image!')
}
Else, if the two cards match you get +1 to your score. These cards then disappear(i.e., a blank card is displayed). We add the matched cards to cardsMatched
array we created before using push()
method. The length of cardsMatched
array is your score.
else if (cardsClicked[0] === cardsClicked[1]) {
cards[firstCard].setAttribute('src', blank)
cards[secondCard].setAttribute('src', blank)
cardsMatched.push(cardsClicked)
}
Once we have a matched, the blank cards are still "clickable" and that's a flaw as far as user experience goes.
So we have to remove the "click" event listener of the matched pairs. The removeEventListener() method removes from the EventTarget an event listener previously registered.
else if (cardsClicked[0] === cardsClicked[1]) {
// previous code
cards[firstCard].removeEventListener('click', flipCard)
cards[secondcard].removeEventListener('click', flipCard)
}
If the next card you flip does not match, the cards flip back. The game continues until you match all the cards on the board.
else {
cards[firstCard].setAttribute('src', placeholder)
cards[secondCard].setAttribute('src', placeholder)
}
We have to add the live score into result
using textContent
. After the logic executed, we have to clear both the cardsClicked
and cardsClickedId
arrays no matter what.
cardsClicked = []
cardsClickedId = []
result.textContent = cardsMatched.length
Our code so far will be:
document.addEventListener('DOMContentLoaded', () => {
const cardArray = [....]
const board = document.querySelector('.board')
const result = document.querySelector('#score')
const placeholder = "https://cloud-5ystxzer7.vercel.app/7placeholder.png"
const blank = "https://cloud-5ystxzer7.vercel.app/6blank.png"
var cardsClicked = []
var cardsClickedId = []
var cardsMatched = []
//creating game board
function createBoard() {....}
//flip the card
function flipCard() {....}
//check for match
function checkForMatch() {
var cards = document.querySelectorAll('img')
const firstCard = cardsClickedId[0]
const secondCard = cardsClickedId[1]
if(firstCard === secondCard) {
cards[firstCard].setAttribute('src', placeholder)
cards[secondCard].setAttribute('src', placeholder)
alert('You have clicked the same image!')
}
else if (cardsClicked[0] === cardsClicked[1]) {
cards[firstCard].setAttribute('src', blank)
cards[secondCard].setAttribute('src', blank)
cards[firstCard].removeEventListener('click', flipCard)
cards[secondCard].removeEventListener('click', flipCard)
cardsMatched.push(cardsClicked)
}
else {
cards[firstCard].setAttribute('src', placeholder)
cards[secondCard].setAttribute('src', placeholder)
}
cardsClicked = []
cardsClickedId = []
result.textContent = cardsMatched.length
if (cardsMatched.length === cardArray.length/2) {
result.textContent = 'Congratulations! You found them all!'
}
}
createBoard()
})
One thing you might notice that the cards are not random. So we have to shuffle the cardArray
, every time before creating the board, using sort()
method. The sort() method sorts the elements of an array in place and returns the sorted array.
Add the following piece of code just after the cardArray
, before the constants we created.
cardArray.sort(() => 0.5 - Math.random())
Finally, we finished our memory game.
Check the final code here.
Hacking
It's your turn to customize.
- Use your creativity and go wild with the styles. You can use the desired theme, and create different card games.
- The game consists of an even number of cards, you can add different levels with the different dimensions of the board, eg:- 3 x 4, 4 x 4, 6 x 4, etc.
- For more functionality of the game, you can add a timer, track the number of moves.
- You can also add two-player mode with more number of cards.
- Also there are many ways of creating the board. If you are good in JavaScript, use
innerHTML
method for adding cards to the board.
Hope you love this workshop! :v:
After customising, do share with me on slack as @Giridhar. I'd love to see your project.
Inspiration
These are some other projects to inspire you.
- Memory Game: Full functionality Memory Game.
Demo. Source Code. - Memory game using animations: The cards animate while flipping, making them 3D. Advanced CSS3 is used in this project.
Demo. Source Code. - Two player Memory Game: This memory card game can be played by two players.
Demo. Source Code.