Umar Zahid
Umar Zahid

Reputation: 17

Cannot use 'keydown' and keyup' event listeners on the same div

I want to be able to start a timer when the space bar is released and to stop it when the space bar is pressed. The code I am using right now initially achieves this. However it does not work on subsequent attempts. I am using a boolean to store whether the key has been pressed in the last .5 seconds (in order to not trigger the event again when the user releases the space bar). This code only works on the first attempt

const timer = document.querySelector('#timer')
const playArea = document.querySelector('#play-area')
let timerStarted = false

// starts timer when spacebar is released
playArea.addEventListener('keyup', (e) => {
    if (e.keyCode === 32 && !timerStarted) startTimer()
})

// start timer function
function startTimer() {
    // assigning time values to zero
    let [milliseconds, seconds, minutes] = [0, 0, 0]
    let Interval
    // actual timer code
    Interval = setInterval(() => {
        milliseconds++
        if (milliseconds % 100 === 0) {
            milliseconds = 0
            seconds++
        }
        if (seconds % 60 === 0 && seconds !== 0) {
            seconds = 0
            minutes++
        }
        timer.innerText = `${minutes}:${seconds}.${milliseconds}`
    }, 10)
    playArea.addEventListener('keydown', (e) => {
        if (e.keyCode === 32) stopTimer(Interval)
    })
}

function stopTimer(interval) {
    clearInterval(interval)
    timerStarted = true
    setTimeout(function () {
        timerStarted = false
        console.log(timerStarted)
    }, 500)
    console.log(timerStarted)
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/public/css/style.css">
    <script src="/public/js/timer.js" defer></script>
</head>

<body>
    <section id="play-area" tabindex="-1">
        <h1 id="timer">00:00</h1>
    </section>
</body>

</html>

Upvotes: 0

Views: 76

Answers (3)

Bhavya Dhiman
Bhavya Dhiman

Reputation: 30

you need to put timerStarted = true inside the keyUp event and the if function. That way it will work correctly.

Upvotes: 0

Shahed
Shahed

Reputation: 1895

I have just changed a little bit of your code.

const timer = document.querySelector("#timer");
const playArea = document.querySelector("#play-area");
let timerStarted = false;
let Interval;
playArea.addEventListener("keypress", (e) => {
  if (e.keyCode == 32) {
    if (!timerStarted) {
      timerStarted = true;
      startTimer();
    } else {
      timerStarted = false;
      stopTimer(Interval);
    }
  }
});
// start timer function
function startTimer() {
  timerStarted = true;
  let [milliseconds, seconds, minutes] = [0, 0, 0];
  Interval = setInterval(() => {
    milliseconds++;
    if (milliseconds % 100 === 0) {
      milliseconds = 0;
      seconds++;
    }
    if (seconds % 60 === 0 && seconds !== 0) {
      seconds = 0;
      minutes++;
    }
    timer.innerText = `${minutes}:${seconds}.${milliseconds}`;
  }, 10);
}

function stopTimer(interval) {
  clearInterval(interval);
}

see the below code

playArea.addEventListener("keyup", (e) => {
  if (e.keyCode == 32 && !timerStarted) {
    console.log("pressed again");
    timerStarted = true;
    startTimer();
  }
});
playArea.addEventListener("keydown", (e) => {
  console.log("click");
  if (e.keyCode == 32) {
    timerStarted = false;
    stopTimer(Interval);
  }
});

Now the timer will stop on key down (hold the spacebar), but simultaneously timer will start again because of you have released key again.

Solution using two keys spacebar to start and s key to stop.

playArea.addEventListener("keyup", (e) => {
  if (e.keyCode == 32 && !timerStarted) {
    console.log("pressed again");
    timerStarted = true;
    startTimer();
  }
});
playArea.addEventListener("keydown", (e) => {

  if (e.keyCode == 83) {
    timerStarted = false;
    stopTimer(Interval);
  }
});

Upvotes: 1

epascarello
epascarello

Reputation: 207537

Your code will add a bunch of event listeners to your element every time you create a new timer instance. Also intervals are not accurate so your time will drift the longer it runs. It is better to use timestamps to determine how long it has been running.

Basic idea

class Timer {

  interval = null
  startTime = null

  constructor(outputElem) {
    this.outputElem = outputElem;
  }
  
  displayOutput () {
    this.outputElem.textContent = this.formatMS(Date.now() - this.startTime);
  }
  
  formatMS (ms) {
    return new Date(ms).toISOString().substring(11, 23);
  }
  
  start () {
    if (this.interval) return;
    this.startTime = Date.now();
    this.displayOutput();
    this.interval = window.setInterval(() => this.displayOutput(), 10);
  }
  
  stop () {
    if (!this.interval) return;
    window.clearInterval(this.interval);
    this.interval = null;
    this.displayOutput();
  }
}

const timerElem = document.querySelector('#timer')
const playArea = document.querySelector('#play-area')

const myTimer = new Timer(timerElem);

playArea.addEventListener('keyup', (e) => {
    if (e.code === "Space") myTimer.start()
});

playArea.addEventListener('keydown', (e) => {
    if (e.code === "Space") myTimer.stop();
});
<section id="play-area" tabindex="-1">
  <h1 id="timer">00:00</h1>
</section>

Upvotes: 1

Related Questions