<h1>Memory Game</h1>
<div class="game-board" id="game-board">
</div>
* {
box-sizing: border-box;
}
h1{
text-align: center;
}
:root {
--x: 6;
--y: 6;
}
.game-board {
display: grid;
grid-template-columns: repeat(var(--x), 1fr);
grid-template-rows: repeat(var(--y), 1fr);
gap: 10px;
max-width: 500px;
margin: auto;
position: relative;
}
.card {
width: 70px;
height: 70px;
position: relative;
border-radius: 8px;
transition: 0.6s;
transform-style: preserve-3d;
cursor: pointer;
}
.card-clicked {
transform: rotateY(180deg);
}
.card-front {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-image: url("https://iili.io/J5ANw2j.md.png");
background-size: 70px 70px;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-o-backface-visibility: hidden;
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg);
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-o-backface-visibility: hidden;
backface-visibility: hidden;
border: 1px solid black;
border-radius: 8px;
}
.empty-card {
width: 70px;
height: 70px;
}
/* CSS */
.button-6 {
position:absolute;
align-items: center;
background-color: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: .25rem;
box-shadow: rgba(0, 0, 0, 0.02) 0 1px 3px 0;
box-sizing: border-box;
color: rgba(0, 0, 0, 0.85);
cursor: pointer;
display: inline-flex;
font-family: system-ui,-apple-system,system-ui,"Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 16px;
font-weight: 600;
justify-content: center;
line-height: 1.25;
margin: 0;
min-height: 3rem;
padding: calc(.875rem - 1px) calc(1.5rem - 1px);
text-decoration: none;
transition: all 250ms;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
vertical-align: baseline;
width: auto;
top:50%;
left:50%;
transform: translate(-50%,-50%);
}
.button-6:hover,
.button-6:focus {
border-color: rgba(0, 0, 0, 0.15);
box-shadow: rgba(0, 0, 0, 0.1) 0 4px 12px;
color: rgba(0, 0, 0, 0.65);
}
.button-6:hover {
transform: translateY(-1px);
}
.button-6:active {
background-color: #F0F0F1;
border-color: rgba(0, 0, 0, 0.15);
box-shadow: rgba(0, 0, 0, 0.06) 0 2px 4px;
color: rgba(0, 0, 0, 0.65);
transform: translateY(0);
}
const board = document.getElementById("game-board");
class Card {
constructor(value) {
this.value = value;
this.element = null;
this.isEmptyCard = false;
this.init();
}
init() {
this.generateFlipCard();
}
generateFlipCard() {
const flipCard = document.createElement("div");
const frontCard = document.createElement("div");
const backCard = document.createElement("div");
flipCard.className = "card";
frontCard.className = "card-front";
backCard.className = "card-back";
flipCard.appendChild(frontCard);
flipCard.appendChild(backCard);
backCard.textContent = this.value;
this.element = flipCard;
}
replaceWith() {
const emptyCard = document.createElement("div");
emptyCard.className = "empty-card";
this.element.replaceWith(emptyCard);
this.element = emptyCard;
this.isEmptyCard = true;
}
toggleClass() {
if (!this.isEmptyCard) {
this.element.classList.toggle("card-clicked");
}
}
getElement() {
return this.element;
}
}
class MemomryBoard {
constructor(size, boardElement) {
this.boardSize = size;
this.boardElement = boardElement;
this.init();
}
init() {
this.cardNumber = [];
this.nodeMap = new WeakMap();
this.viewedCards = [];
this.timer = null;
this.disableClick = false;
this.removedCards = 0;
this.boardElement.innerHTML='';
this.generateCardNumber();
this.shuffleCards();
this.addToDom();
}
generateCardNumber() {
const cardNumber = (this.boardSize * this.boardSize) / 2;
for (let i = 1; i <= cardNumber; i++) {
const cardOne = new Card(i);
const cardTwo = new Card(i);
this.nodeMap.set(cardOne.getElement(), cardOne);
this.nodeMap.set(cardTwo.getElement(), cardTwo);
this.cardNumber.push(cardOne);
this.cardNumber.push(cardTwo);
}
}
addToDom() {
const fragment = document.createDocumentFragment();
for (let i = 0; i < this.cardNumber.length; i++) {
fragment.appendChild(this.cardNumber[i].getElement());
}
this.boardElement.appendChild(fragment);
this.bindEvents();
}
shuffleCards() {
const length = this.cardNumber.length;
for (let i = 0; i < length; i++) {
const index = Math.floor(Math.random() * length);
const temp = this.cardNumber[i];
this.cardNumber[i] = this.cardNumber[index];
this.cardNumber[index] = temp;
}
}
startTimer(){
this.timer = setTimeout(this.timerCallback.bind(this),3000);
}
timerCallback(){
const length = this.viewedCards.length;
if(length === 2){
const [cardOne,cardTwo] = this.viewedCards;
if(cardOne.value === cardTwo.value){
cardOne.replaceWith();
cardTwo.replaceWith();
this.removedCards+=2;
}else {
cardOne.toggleClass();
cardTwo.toggleClass();
}
this.viewedCards = [];
this.disableClick = false;
this.checkIfMatchCompleted();
}else if(length === 1){
const card = this.viewedCards.pop();
card.toggleClass();
}
}
bindEvents(){
this.boardElement.addEventListener("click", (event) => {
if(!this.disableClick){
const parentElement = event.target.parentElement;
const haveClass = parentElement.classList.contains('card-clicked');
if (this.nodeMap.has(parentElement)&&!haveClass) {
const card = this.nodeMap.get(parentElement);
this.viewedCards.push(card);
if(this.viewedCards.length ===2){
this.disableClick = true;
}
card.toggleClass();
clearTimeout(this.timer);
this.startTimer();
}
}
});
}
checkIfMatchCompleted(){
if(this.removedCards === this.boardSize*this.boardSize){
this.boardElement.innerHTML = '';
const button = document.createElement('button');
button.className = 'button-6';
button.textContent = 'Play Again';
this.boardElement.appendChild(button);
button.addEventListener('click',()=>{
this.init();
})
}
}
}
const Game = new MemomryBoard(6, board);
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.