Skip to content

How to show random images in random grid child for vanilla JS whack-a-mole

I´m working in a whack-a-mole game in vanilla JS (following this tutorial https://www.youtube.com/watch?v=rJU3tHLgb_c&t=1s) and I would like to display a random image every time the mole appears on the screen instead of the same mole image. Right now what I get is the same random image repeated in each square. This is not what I want and I don´t understand why this is happening. I would appreciate any help!

const squares = document.querySelectorAll('.square');
const mole = document.querySelector('.mole');
const timeLeft = document.querySelector('#time-left');
const score = document.querySelector('#score');
const bgColorArray = ['https://res.cloudinary.com/dkwiijqr0/image/upload/v1630402761/Whack-a-mole/sample-mole_in4rlk.png', 'https://res.cloudinary.com/dkwiijqr0/image/upload/v1630347923/Whack-a-mole/mole-img_antxud.png'],
  selectBG = bgColorArray[Math.floor(Math.random() * bgColorArray.length)];

let result = 0;
let hitPosition;
let currentTime = 60;
let timerId = null;

function randomSquare() {
  squares.forEach((square) => {
    square.classList.remove('mole');
  });
  
  let randomSquare = squares[Math.floor(Math.random() * 9)];
  randomSquare.classList.add('mole');

  hitPosition = randomSquare.id;

  document.querySelector(".mole").style.backgroundImage = 'url(' + selectBG + ')';
}

squares.forEach((square) => {
  square.addEventListener('mousedown', () => {
    if (square.id == hitPosition) {
      result++;
      score.textContent = result;
      hitPosition = null;
    }
  });
});

function moveMole() {
  timerId = setInterval(randomSquare, 500);
}

moveMole();

function countDown() {
  currentTime--;
  timeLeft.textContent = currentTime;

  if (currentTime == 0) {
    clearInterval(countDownTimerId);
    clearInterval(timerId);
    alert('GAME OVER! Your final score is ' + result);
  }
}

let countDownTimerId = setInterval(countDown, 1000);
.grid {
  width: 606px;
  height: 606px;
  display: flex;
  flex-wrap: wrap;
}

.square {
  height: 200px;
  width: 200px;
  border: solid black 1px;
}

.mole {
  background-size: cover;
}
<h2>Your score:</h2>
<h2 id="score">0</h2>

<h2>Time Left:</h2>
<h2 id="time-left">60</h2>

<div class="grid">
  <div class="square" id="1"></div>
  <div class="square" id="2"></div>
  <div class="square" id="3"></div>
  <div class="square" id="4"></div>
  <div class="square" id="5"></div>
  <div class="square" id="6"></div>
  <div class="square" id="7"></div>
  <div class="square" id="8"></div>
  <div class="square" id="9"></div>
</div>

Answer

You just have to move the definition of selectBG in the function randomSquare(). Otherwise you will always use the same selectBG which you defined as constant at the beginning (where the random function runs only once).

If you only want one background image to be visible you have to reset the backgrounds for all squares at that point where you remove the class .mole from all squares.

squares.forEach((square) => {
  square.classList.remove('mole');
  square.style.backgroundImage = '';
});

Working example:

const squares = document.querySelectorAll('.square');
const mole = document.querySelector('.mole');
const timeLeft = document.querySelector('#time-left');
const score = document.querySelector('#score');
const bgColorArray = ['https://res.cloudinary.com/dkwiijqr0/image/upload/v1630402761/Whack-a-mole/sample-mole_in4rlk.png', 'https://res.cloudinary.com/dkwiijqr0/image/upload/v1630347923/Whack-a-mole/mole-img_antxud.png'];

let result = 0;
let hitPosition;
let currentTime = 60;
let timerId = null;

function randomSquare() {
  squares.forEach((square) => {
    square.classList.remove('mole');
    square.style.backgroundImage = '';
  });
  
  let randomSquare = squares[Math.floor(Math.random() * 9)];
  randomSquare.classList.add('mole');

  let selectBG = bgColorArray[Math.floor(Math.random() * bgColorArray.length)];

  hitPosition = randomSquare.id;

  document.querySelector(".mole").style.backgroundImage = 'url(' + selectBG + ')';
}

squares.forEach((square) => {
  square.addEventListener('mousedown', () => {
    if (square.id == hitPosition) {
      result++;
      score.textContent = result;
      hitPosition = null;
    }
  });
});

function moveMole() {
  timerId = setInterval(randomSquare, 500);
}

moveMole();

function countDown() {
  currentTime--;
  timeLeft.textContent = currentTime;

  if (currentTime == 0) {
    clearInterval(countDownTimerId);
    clearInterval(timerId);
    alert('GAME OVER! Your final score is ' + result);
  }
}

let countDownTimerId = setInterval(countDown, 1000);
.grid {
  width: 606px;
  height: 606px;
  display: flex;
  flex-wrap: wrap;
}

.square {
  height: 200px;
  width: 200px;
  border: solid black 1px;
}

.mole {
  background-size: cover;
}
<h2>Your score:</h2>
<h2 id="score">0</h2>

<h2>Time Left:</h2>
<h2 id="time-left">60</h2>

<div class="grid">
  <div class="square" id="1"></div>
  <div class="square" id="2"></div>
  <div class="square" id="3"></div>
  <div class="square" id="4"></div>
  <div class="square" id="5"></div>
  <div class="square" id="6"></div>
  <div class="square" id="7"></div>
  <div class="square" id="8"></div>
  <div class="square" id="9"></div>
</div>