Search code examples
javascriptdom-eventsaddeventlistenerkeyevent

Event listener not working when called outside of a recurring function, but being declared over and over again when declared inside of one?


I am making sort of a "catch the egg in the basket" game, and my controls using arrow keys, run by event listeners, will get progressively more sensitive as there are more and more event listeners that move the paddle, because they are declared inside of the function that sets the game's framerate, each moving the paddle, but when I put the event listener outside of the function, it doesn't work. Any suggestions? this is my code.

var canvas = document.getElementById("ok");
var ctx = canvas.getContext("2d");
var loggingMachine = document.getElementById("loggy");
var score = 0;

const Rg = Math.floor(Math.random() * 480);
function ball(x) {
  this.x = x;
  this.y = 0;
  this.width = 20;
  this.height = 20;
}
var firstBall = new ball(Rg);

function newBall() {
  if (firstBall.y < 480) {
    ctx.clearRect(0, 0, 480, 480);
    ctx.fillRect(firstBall.x, firstBall.y, firstBall.width, firstBall.height);
    firstBall.y++;
  } else {
    const playerStart = player.x;
    const playerEnd = player.x + player.width;

    const ballStart = firstBall.x;
    const ballEnd = firstBall.x + firstBall.width;

    const test1 = ballEnd - playerStart;
    const test2 = ballStart - playerEnd;

    if (test1 >= 0 && test2 <= 0) {
      score++;
      loggingMachine.innerText = score;
    } else {
      score = 0;
    }
    firstBall.y = 0;
    firstBall.x = Math.floor(Math.random() * 480);
  }
}
function paddle(x) {
  this.x = x;
  this.y = 470;
  this.width = 70;
  this.height = 20;
}

var player = new paddle(50);
function renderPlayer() {
  ctx.fillRect(player.x, player.y, player.width, player.height);
}
function tick() {
  newBall(Rg);

  function logKey(e) {
    if (e.code === "ArrowLeft") {
      if (player.x > 0) {
        player.x = player.x - 0.05;
      }
    } else if (e.code === "ArrowRight") {
      if (player.x + player.width < 480) {
        player.x = player.x + 0.05;
      }
    }
  }
  document.addEventListener("keydown", logKey);

  renderPlayer();
  setTimeout(tick, 5);
}
loggingMachine.innerText = score;

tick();
<!DOCTYPE html>
<canvas id="ok" width="480" height="480"></canvas>
<p id="loggy"></p>
<script src="script.js"></script>


Solution

  • You added an event-listener every tick() without removing the old one. You should set it outside like follow:

    var canvas = document.getElementById("ok");
    var ctx = canvas.getContext("2d");
    var loggingMachine = document.getElementById("loggy");
    var score = 0;
    
    const Rg = Math.floor(Math.random() * 480);
    function ball(x) {
      this.x = x;
      this.y = 0;
      this.width = 20;
      this.height = 20;
    }
    var firstBall = new ball(Rg);
    
    function newBall() {
      if (firstBall.y < 480) {
        ctx.clearRect(0, 0, 480, 480);
        ctx.fillRect(firstBall.x, firstBall.y, firstBall.width, firstBall.height);
        firstBall.y++;
      } else {
        const playerStart = player.x;
        const playerEnd = player.x + player.width;
    
        const ballStart = firstBall.x;
        const ballEnd = firstBall.x + firstBall.width;
    
        const test1 = ballEnd - playerStart;
        const test2 = ballStart - playerEnd;
    
        if (test1 >= 0 && test2 <= 0) {
          score++;
          loggingMachine.innerText = score;
        } else {
          score = 0;
        }
        firstBall.y = 0;
        firstBall.x = Math.floor(Math.random() * 480);
      }
    }
    function paddle(x) {
      this.x = x;
      this.y = 470;
      this.width = 70;
      this.height = 20;
    }
    
    var player = new paddle(50);
    function renderPlayer() {
      ctx.fillRect(player.x, player.y, player.width, player.height);
    }
    function tick() {
      newBall(Rg);     
    
      renderPlayer();
      setTimeout(tick, 5);
    }
    loggingMachine.innerText = score;
    
    document.addEventListener("keydown", (e) => {
        if (e.code === "ArrowLeft") {
          if (player.x > 0) {
            player.x = player.x - 5;
          }
        } else if (e.code === "ArrowRight") {
          if (player.x + player.width < 480) {
            player.x = player.x + 5;
          }
        }
    });
    
    tick();
    <!DOCTYPE html>
    <canvas id="ok" width="480" height="480"></canvas>
    <p id="loggy"></p>
    <script src="script.js"></script>

    Your problem: Before with multiple listeners the x-coord was updated multiple times with 0.05px in one direction. which resulted in something like: 0.05 px * 100 listeners = 5 px

    Now it will only be updated once with 0.05 px in one direction. this is so slow that you cannot see it :-) Increase the step to 5px and it works like a charm.

    Tip: Try to learn how to debug your code