2

I have been working on a whack-a-mole project over the last couple weeks and I ran in to an issue maybe a week ago when trying to click on new mole images that are generated. The goal here is to simply generate an image of a mole in a gamespace div, and each mole can be clicked to increase the user's score. When I run the program I am able to click on the first mole image to increase the score counter, but all other moles are not clickable. There are some other functions in here such as randomX() that are not used in this stage. I used those later for generating images in random locations. I would really appreciate any help, thanks in advance!

var userScore = 0; //Global variable for score counter
var gameTimer = 30; //Global variable for game timer

$(document).ready(function() {
  $("#start_button").click(function() {
    gameStart();
    $("#mole").on('click', scoreIncrease);
  });
});

function randomY() { //function to calcualte random y-value between 0 and 300
  var y = Math.floor((Math.random() * 300) + 0);
  return y;
}

function randomX() { //function to calculate random x-value between 0 and 600
  var x = Math.floor((Math.random() * 600) + 0);
  return x;
}

function scoreIncrease() { //function that adds to user score and updates #score element
  userScore++;
  $('#score').html(userScore + ' pts');
}

function timerDecrease() { //function that decrements gameTimer and sets text for #timer
  $("#timer").html(gameTimer + ' seconds left');
  gameTimer--;
  setTimeout("timerDecrease()", 1000);
}

function addMole() {
  var t = $('#gamespace').append('<img id="mole" src="img/mole.jpg" />');
  t = setTimeout(addMole, 3000);
}

function gameStart() {
  $('#timer').show(); //show timer
  addMole(); //call addMole function
  $('#gamespace').css('background-color', 'brown'); //change #gamespace background color
  $('#content').css('background-color', 'green'); //change #content background color
  timerDecrease();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Swink
  • 353
  • 3
  • 26

2 Answers2

1

When you run this line:

$("#mole").on('click', scoreIncrease);

You are selecting all #mole elements, then attaching click handlers to each one. This means that if a #mole element is added in the future, it won't have any handler attached to it. You need to instead attach a handler to a parent element and filter it by a selector, like so:

$("#gamespace").on("click", "#mole", scoreIncrease);

This attaches the actual handler to #gamespace, then checks every time you click to see if you a clicked on a #mole. Additionally, I don't see anywhere in your code that you're removing clicked moles - if it's possible to have more than one on the screen at a time, you'll need to use a class (.mole) instead of an ID.

Hydrothermal
  • 4,851
  • 7
  • 26
  • 45
  • This is exactly what needed to be done. As far as removing moles, that is something I will be working on next now that I have this figured out. Thanks for not only correcting me but also for explaining what I did wrong. – DreadlordDave Dec 15 '18 at 20:31
  • @DreadlordDave No problem. Don't forget to mark the answer as accepted so other responders know additional answers aren't needed. Another note - you have the line `setTimeout("timerDecrease()", 1000);`. You should avoid using a string as the argument like that and instead copy your addMole timeout. Good luck! – Hydrothermal Dec 15 '18 at 20:33
  • done and done, thanks again! – DreadlordDave Dec 15 '18 at 20:41
0

I had a go at writing a pure js basic whack-a-mole game because I thought it sounded like fun. It's not very sophisticated and it doesn't have a countdown timer or "new game" button, but some of the logic might be helpful to you.

// start game IIFE
(function runGame() {
  var game_length = 10000;
  var duration = 0;
  round(duration, game_length, 0);
})();


function round(duration, game_length) {

 // round length between 0.5-1.5 seconds
  var timeout = 500 + getRandomUpTo(1000);
  duration += timeout;
 
  window.setTimeout(function() {

  // get the target from the previous round if any
    var old_target = document.getElementsByClassName('target')[0];
    if( old_target ){
      // remove classes and click handler
      old_target.classList.remove('hit');
       old_target.classList.remove('target');
      old_target.removeEventListener('click',hit_target);
    }

  // randomly select a new hole to be the target
    var hole_num = getRandomUpTo(16);
    var new_target = document.getElementsByClassName('hole' + hole_num)[0];
    new_target.classList.add('target');
    // add the click handler for successful hit
    new_target.addEventListener('click', hit_target )

  // if we've got time for another round, call recursively
    if (duration < game_length) {
      return round(duration, game_length);
    }

  }, timeout);

}

// click handler for successful hits
function hit_target(event){
  
  // update score
  var score_elem = document.getElementsByClassName('score_num')[0];
  var score = score_elem.dataset.score;
  score++;
  score_elem.dataset.score = score;
  score_elem.innerHTML = score;
  
 // turn green so we know we hit it
  this.classList.add('hit');
  // remove event listener so we can only hit it once
  this.removeEventListener('click',hit_target);
}

// helper function for getting random numbers
function getRandomUpTo(max) {
  return Math.floor(Math.random() * max) + 1;
}
.board {
  border: 2px solid #ccc;
  width: 400px;
  float: left;
}

.hole {
  box-sizing: border-box;
  border-radius: 50%;
  height: 80px;
  width: 80px;
  margin: 10px;
  background-color: #333;
  float: left;
  cursor: pointer;
}

.target {
  background-color: red;
}

.hit {
  background-color: green !important;
}

.score {
  display: inline-block;
  border: 2px solid #ccc;
  padding: 20px;
  margin: 0 10px;
  font-size: 30px;
  font-weight: 800;
  font-family: helvetica, aria, sans-serif;
}

.score_num {
  margin-left: 5px;
}
<div class="board">
  <div class="hole hole1"></div>
  <div class="hole hole2"></div>
  <div class="hole hole3"></div>
  <div class="hole hole4"></div>
  <div class="hole hole5"></div>
  <div class="hole hole6"></div>
  <div class="hole hole7"></div>
  <div class="hole hole8"></div>
  <div class="hole hole9"></div>
  <div class="hole hole10"></div>
  <div class="hole hole11"></div>
  <div class="hole hole12"></div>
  <div class="hole hole13"></div>
  <div class="hole hole14"></div>
  <div class="hole hole15"></div>
  <div class="hole hole16"></div>
</div>
<div class="score"><span>Score:</span><span class="score_num" data-score="0">0</span></div>
sauntimo
  • 1,531
  • 1
  • 17
  • 28
  • probably worth running this snippet in the full page view because it's a bit too large for the small preview it generates. – sauntimo Dec 15 '18 at 21:38