In this tutorial, I will teach you how to make a Memory Cards Game with Timer using HTML5, CSS3, and JavaScript. The complete source code of this JavaScript Memory Cards Game with a Time Limit functionality is given below.
You can download the full source code (including images) of this JavaScript Memory Cards Game with Timer at the end of this article.
<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>Memory Cards Game with Timer</title> <link rel="stylesheet" href="style.css"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div class="wrapper"> <ul class="cards"> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-1.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-2.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-3.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-4.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-5.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-6.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-5.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-6.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-1.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-2.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-3.png" alt="card-img"> </div> </li> <li class="card"> <div class="view front-view"> <img src="images/que_icon.svg" alt="icon"> </div> <div class="view back-view"> <img src="images/img-4.png" alt="card-img"> </div> </li> <div class="details"> <p class="time">Time: <span><b>20</b>s</span></p> <p class="flips">Flips: <span><b>0</b></span></p> <button>Refresh</button> </div> </ul> </div> <script src="script.js"></script> </body> </html>
const cards = document.querySelectorAll(".card"), timeTag = document.querySelector(".time b"), flipsTag = document.querySelector(".flips b"), refreshBtn = document.querySelector(".details button"); let maxTime = 20; let timeLeft = maxTime; let flips = 0; let matchedCard = 0; let disableDeck = false; let isPlaying = false; let cardOne, cardTwo, timer; function initTimer() { if(timeLeft <= 0) { return clearInterval(timer); } timeLeft--; timeTag.innerText = timeLeft; } function flipCard({target: clickedCard}) { if(!isPlaying) { isPlaying = true; timer = setInterval(initTimer, 1000); } if(clickedCard !== cardOne && !disableDeck && timeLeft > 0) { flips++; flipsTag.innerText = flips; clickedCard.classList.add("flip"); if(!cardOne) { return cardOne = clickedCard; } cardTwo = clickedCard; disableDeck = true; let cardOneImg = cardOne.querySelector(".back-view img").src, cardTwoImg = cardTwo.querySelector(".back-view img").src; matchCards(cardOneImg, cardTwoImg); } } function matchCards(img1, img2) { if(img1 === img2) { matchedCard++; if(matchedCard == 6 && timeLeft > 0) { return clearInterval(timer); } cardOne.removeEventListener("click", flipCard); cardTwo.removeEventListener("click", flipCard); cardOne = cardTwo = ""; return disableDeck = false; } setTimeout(() => { cardOne.classList.add("shake"); cardTwo.classList.add("shake"); }, 400); setTimeout(() => { cardOne.classList.remove("shake", "flip"); cardTwo.classList.remove("shake", "flip"); cardOne = cardTwo = ""; disableDeck = false; }, 1200); } function shuffleCard() { timeLeft = maxTime; flips = matchedCard = 0; cardOne = cardTwo = ""; clearInterval(timer); timeTag.innerText = timeLeft; flipsTag.innerText = flips; disableDeck = isPlaying = false; let arr = [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]; arr.sort(() => Math.random() > 0.5 ? 1 : -1); cards.forEach((card, index) => { card.classList.remove("flip"); let imgTag = card.querySelector(".back-view img"); setTimeout(() => { imgTag.src = `images/img-${arr[index]}.png`; }, 500); card.addEventListener("click", flipCard); }); } shuffleCard(); refreshBtn.addEventListener("click", shuffleCard); cards.forEach(card => { card.addEventListener("click", flipCard); });
/* Import Google Font - Poppins */@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); *{ margin: 0; padding: 0; box-sizing: border-box; font-family: 'Poppins', sans-serif; } p{ font-size: 20px; } body{ display: flex; align-items: center; justify-content: center; min-height: 100vh; background: #6563ff; } ::selection{ color: #fff; background: #6563ff; } .wrapper{ padding: 25px; background: #f8f8f8; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); } .cards, .card, .view, .details, p{ display: flex; align-items: center; justify-content: center; } .cards{ height: 350px; width: 350px; flex-wrap: wrap; justify-content: space-between; } .cards .card{ cursor: pointer; position: relative; perspective: 1000px; transform-style: preserve-3d; height: calc(100% / 4 - 10px); width: calc(100% / 4 - 10px); } .card.shake{ animation: shake 0.35s ease-in-out; } @keyframes shake { 0%, 100%{ transform: translateX(0); } 20%{ transform: translateX(-13px); } 40%{ transform: translateX(13px); } 60%{ transform: translateX(-8px); } 80%{ transform: translateX(8px); } } .cards .card .view{ width: 100%; height: 100%; user-select: none; pointer-events: none; position: absolute; background: #fff; border-radius: 7px; backface-visibility: hidden; transition: transform 0.25s linear; box-shadow: 0 3px 10px rgba(0,0,0,0.1); } .card .front-view img{ max-width: 17px; } .card .back-view{ transform: rotateY(-180deg); } .card .back-view img{ max-width: 40px; } .card.flip .front-view{ transform: rotateY(180deg); } .card.flip .back-view{ transform: rotateY(0); } .details{ width: 100%; margin-top: 15px; padding: 0 20px; border-radius: 7px; background: #fff; height: calc(100% / 4 - 30px); justify-content: space-between; box-shadow: 0 3px 10px rgba(0,0,0,0.1); } .details p{ font-size: 18px; height: 17px; padding-right: 18px; border-right: 1px solid #ccc; } .details p span{ margin-left: 8px; } .details p b{ font-weight: 500; } .details button{ cursor: pointer; font-size: 14px; color: #6563ff; border-radius: 4px; padding: 4px 11px; background: #fff; border: 2px solid #6563ff; transition: 0.3s ease; } .details button:hover{ color: #fff; background: #6563ff; } @media screen and (max-width: 700px) { .cards{ height: 350px; width: 350px; } .card .front-view img{ max-width: 16px; } .card .back-view img{ max-width: 40px; } } @media screen and (max-width: 530px) { .cards{ height: 300px; width: 300px; } .card .back-view img{ max-width: 35px; } .details{ margin-top: 10px; padding: 0 15px; height: calc(100% / 4 - 20px); } .details p{ height: 15px; font-size: 17px; padding-right: 13px; } .details button{ font-size: 13px; padding: 5px 10px; border: none; color: #fff; background: #6563ff; } }
MiniMax-M1 is a new open-weight large language model (456 B parameters, ~46 B active) built with hybrid…
Managing Git hooks manually can quickly become tedious and error-prone—especially in fast-moving JavaScript or Node.js…
Git hooks help teams enforce code quality by automating checks at key stages like commits…
Choosing the right Git hooks manager directly impacts code quality, developer experience, and CI/CD performance.…
We evaluated the performance of Llama 3.1 vs GPT-4 models on over 150 benchmark datasets…
The manufacturing industry is undergoing a significant transformation with the advent of Industrial IoT Solutions.…