25 + 5 Clock - Why Does Test 8 in Content Fail?

Hi, everyone.

So I started working on the 25 + 5 Clock yesterday (and I’m going to be using jQuery for this instead of React). I think I have the markup done but apparently the #time-left element has the time formatted wrong. So test 8 in the “Content” section of the tests fails with this error:

time-left is not formatted correctly: expected '60' to equal '25'

The test in question is:

8. I can see an element with corresponding id="time-left". NOTE: Paused or running, the value in this field should always be displayed in mm:ss format (i.e. 25:00).

Here’s my HTML:

<!DOCTYPE html>
<html lang="en-us">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <meta name="author" content="Osman Zakir" />
  <title>DragonOsman 25 + 5 Clock Project</title>
  <link rel="stylesheet" href="styles/style.css" />
  <link rel="shortcut icon" href="img/favicon.ico" />
  <link rel="icon" type="image/gif" href="img/animated_favicon1.gif" />
</head>
<body>
  <main>
    <h1>DragonOsman 25 + 5 Clock</h1>
    <div id="break-label" class="break-label">
      Break Length
      <p id="break-length" class="break-length">5</p>
      <button id="break-decrement" class="break-decrement"></button>
      <button id="break-increment" class="break-increment"></button>
    </div>
    <div id="session-label" class="session-label">
      Session Length
      <p id="session-length" class="session-length">25</p>
      <button id="session-decrement" class="session-decrement"></button>
      <button id="session-increment" class="session-increment"></button>
    </div>
    <div id="timer" class="timer">
      <!--"timer type" refers to whether it's a session or a break; will change 
        using JavaScript as needed-->
      <p id="timer-label" class="timer-label">Session</p>
      <p id="time-left" class="time-left">60:00</p>
    </div>
    <button id="start_stop" class="start_stop"></button>
    <button id="reset" class="reset"></button>
  </main>
  <script
    src="https://code.jquery.com/jquery-3.5.1.min.js"
    integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
    crossorigin="anonymous">
  </script>
  <script 
    src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"
    crossorigin="anonymous">
  </script>
  <script src="scripts/script.js"></script>
</body>
</html>

I have a p element with ID time-left that has time formatted as mm:ss in it. That should pass the test, but it doesn’t and I’ve tried both “25:00” and “60:00” (when I have one, the assertion error says it expected the other). So what am I doing wrong? Could it be that I shouldn’t use a p element (what should I use in that case?)?

We really need a link to the project (codepen, codesandbox, etc.) to see what your script.js is actually doing and to read the test output, but I think a common mistake is when using a Date() object to help format the time, Date().getMinutes() is called and there are 60 minutes, the Date() object has stored that as 1 hour and 0 minutes and returns 0. The test could also be failing if it finds the 60:00 in your html and is expecting a default of 25:00.

I used a <p> for my time-left and it worked.

I haven’t written any JS yet. If I had, I would’ve shown it here. I just wanted to get the HTML markup down first. But if I have to get the JS working for that to pass the test, then I’ll try that and get back to you.

1 Like

That’s it then. That test has several steps and the test suite is looking for the correct format in multiple situations.

I put my code into a Fiddle. I’ll need help on the grid layout in the CSS later, but for now I want to work on the functionality.

I have the increment and decrement buttons working (nothing will happen to the timer yet, though).

Next I’d like to work on the timer, but first I want to know if I should use Date(). And if so, how do I get the correct number of minutes? All I know is I need to use setInterval to create the timer and pass in 1000 as the second argument to setInterval to be the number of milliseconds, so that the timer decreases after every second. Would someone help me out here? I’m not asking for the solution.

I used Date() to start with, which is how I know about the bug I was talking about earlier. Using react, I kept as state the break and session lengths in minutes and the time remaining for the timer in seconds, all as integers. Then I just used integer division to get the minutes and the remainder to get seconds (this was in a function that accepted seconds as the argument and returned the time remaining string), and used a timer like you describe to decrement the number of seconds remaining.

I’m fairly sure this logic will work with react or jquery either one.

1 Like

Did you keep Date() and just find a way to fix the bug after you found out about it?

"use strict";

const breakDecrementButton = $("#break-decrement");
breakDecrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  lengthValue--;
  breakLength.text(lengthValue);
});

const breakIncrementButton = $("#break-increment");
breakIncrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  lengthValue++;
  breakLength.text(lengthValue);
});

const sessionDecrementButton = $("#session-decrement");
sessionDecrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  lengthValue--;
  sessionLength.text(lengthValue);
});

const sessionIncrementButton = $("#session-increment");
sessionIncrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  lengthValue++;
  sessionLength.text(lengthValue);
});

const defaultSessionLength = 25;
const defaultBreakLength = 5;

// default session and break length in seconds
const sessionSeconds = defaultSessionLength * 60;
const breakSeconds = defaultBreakLength * 60;

let timeRemaining = Number($("#time-left").text());

const timerCallback = () => {
  let timer = new Date().getMinutes();

  if ($("#timer-label").text() === "Session") {
    timer += 1;
    timer *= defaultSessionLength;
  } else if ($("#timer-label").text === "Break") {
    timer += 1;
    timer *= defaultBreakLength;
  }

  timeRemaining = Number(timer);
};

setInterval(timerCallback, 1000);

const timerId = setInterval(timerCallback, 1000);

const startStopButton = $("#start_stop");
startStopButton.on("click", () => {
  
});

Am I on the right track so far? And from here, how do I get the timer in the right format in the callback function? And the number of minutes and seconds

I’ll write the code for when the timer reaches 0 in the same function, but first I think I should get this done.

I think I need to use clearInterval to stop the timer, but how do I have it stopped on page load? And in my callback function for the start-stop button click event, how can I tell if the timer is started or stopped? So I can write code to call clearInterval when I need to stop or start the timer.

I just stored the time in seconds and formatted it manually (convert to minutes with division, seconds being the remainder, and zero padding with String.prototype.padStart()). And to be fair to Date(), it was behaving as documented, and I knew it behaved that way with Date.prototype.getMinutes() and forgot while I was working.

In general this looks like it’s going in the right direction. One thing that stands out is your break and session increment/decrement functions are not implementing their constraints (1 <= length <= 60). As far as starting the timer, I stored state to indicate if a session was running and called setInterval()/clearInterval() from the start_stop button depending on the state.

Here’s the Fiddle again, with updated code.

Did I do the timer calculation or wrong is there something I should’ve done that I didn’t?

I think I get why that happened. One mistake I made, among many, was to put the timer initialization code inside the callback function for setInterval. I need to only decrement the timer inside the callback function, right? But on that note, I don’t get how to get the number of seconds to start at 0, then go to 59 and count back down to 0. I know I need to do that every second and have the minutes decrease by 1 each time the seconds reach 0. I probably need two calls to setInterval for this; one using milliseconds argument of 1000 for the seconds, and another with 60000 as the milliseconds argument for the minutes. Or is there a way to do this inside the same setInterval call?

Just keep the 1000ms interval, and store your time values as seconds. Your internal logic only needs seconds, you’ll have to manually convert them to another format ( minutes, or minutes:seconds) to display them.
Minutes are seconds / 60, and for minutes:seconds, you’ll need the remainder of seconds % 60.
And if seconds is 0, you’ll set it back to whatever it should be set to, depending on whether it’s break time or session time.

I need some help here.

Here’s my JS code:

"use strict";

const breakDecrementButton = $("#break-decrement");
try {
  breakDecrementButton.on("click", () => {
    const breakLength = $("#break-length");
    let lengthValue = Number(breakLength.text());
    if (lengthValue > 1) {
      lengthValue--;
    } else if (lengthValue < 1) {
      throw "lengthValue must not be less than 1";
    }
    breakLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const breakIncrementButton = $("#break-increment");
try {
  breakIncrementButton.on("click", () => {
    const breakLength = $("#break-length");
    let lengthValue = Number(breakLength.text());
    if (lengthValue < 60) {
      lengthValue++;
    } else if (lengthValue === 60) {
      throw "lengthValue must be greater than 60";
    }
    breakLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const sessionDecrementButton = $("#session-decrement");
try {
  sessionDecrementButton.on("click", () => {
    const sessionLength = $("#session-length");
    let lengthValue = Number(sessionLength.text());
    if (lengthValue > 1) {
      lengthValue--;
    } else if (lengthValue < 1) {
      throw "lengthValue must not be less than 1";
    }
    sessionLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const sessionIncrementButton = $("#session-increment");
try {
  sessionIncrementButton.on("click", () => {
    const sessionLength = $("#session-length");
    let lengthValue = Number(sessionLength.text());
    lengthValue++;
    sessionLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const defaultSessionLength = 25;
const defaultBreakLength = 5;
const actualSessionLength = $("#session-length").text();
const actualBreakLength = $("#break-length").text();

// default session and break length in seconds
const sessionSeconds = actualSessionLength * 60;
const breakSeconds = actualBreakLength * 60;

let isSessionRunning = true;
let isTimerRunning = false;

const timerElem = $("#time-left");
let timerTextContent = $("#time-left").text();

let totalSeconds = (isSessionRunning) ? sessionSeconds : breakSeconds;

let minutes = parseInt(totalSeconds) / 60;
let seconds = parseInt(totalSeconds) % 60;
const initializeTimer = () => {
  // timer value should be 0 at first, so add 1 and then multiply
  if ($("#timer-label").text() === "Session") {
    totalSeconds += 1;
    totalSeconds *= actualSessionLength;
    isSessionRunning = true;
  } else if ($("#timer-label").text === "Break") {
    totalSeconds += 1;
    totalSeconds *= actualBreakLength;
    isSessionRunning = false;
  }

  if (minutes < 10) {
    minutes.toString().padStart(2, "0");
  }

  if (seconds < 10) {
    seconds.toString().padStart(2, "0");
  }

  timerTextContent = `${minutes}:${seconds}`;
  timerElem.text(timerTextContent);
  isTimerRunning = true;
};

const timerCallback = () => {
  if (minutes === 0 && seconds === 0) {
    if ($("#timer-label").text() === "Session") {
      isSessionRunning = false;
    } else if ($("#timer-label").text() === "Break") {
      isSessionRunning = true;
    }
    
    const audioElem = $("#beep");
    audioElem.play();
  }

  totalSeconds--;
  initializeTimer();
};

const timerId = setInterval(timerCallback, 1000);

const startStopButton = $("#start_stop");
startStopButton.on("click", () => {
  if (isTimerRunning) {
    clearInterval(timerId);
  } else {
    setInterval(timerCallback, 1000);
  }
});

const resetButton = $("#reset");
resetButton.on("click", () => {
  // reset app state to default
  clearInterval(timerId);
  const sessionLength = $("#session-length");
  sessionLength.text(defaultSessionLength);

  const breakLength = $("#break-length");
  breakLength.text(defaultBreakLength);

  totalSeconds = sessionSeconds;
  
  minutes = parseInt(totalSeconds) / 60;
  seconds = parseInt(totalSeconds) % 60;
  if (minutes < 10) {
    minutes.toString().padStart(2, "0");
  }

  if (seconds < 10) {
    seconds.toString().padStart(2, "0");
  }

  timerTextContent = `${minutes}:${seconds}`;
  timerElem.text(timerTextContent);
  isTimerRunning = false;
});

When I click on the “start-stop” button, the value in the #time-left element becomes 25:0, which is probably also what’s making test 8 fail. What did I do wrong?

The calls to padStart() return the string. You’re not returning it to a variable, so when you set timerTextContext, it uses the values in minutes and seconds.

I doubt this is going to fix all your problems, but you call your initializeTimer() function every time your timerCallback runs. So, timerCallback decreases the totalSeconds by 1, and initializeTimer() increases totalSeconds by 1 (before multiplying it with the actualSessionLength). Does your timer run at all? Or does it run up really fast instead of down?

I took out the part where I was adding 1. But of course I still have that issue where single digit time values aren’t being padded by 0 at the beginning.

Updated JS code:

"use strict";

const breakDecrementButton = $("#break-decrement");
try {
  breakDecrementButton.on("click", () => {
    const breakLength = $("#break-length");
    let lengthValue = Number(breakLength.text());
    if (lengthValue > 1) {
      lengthValue--;
    } else if (lengthValue < 1) {
      throw "lengthValue must not be less than 1";
    }
    breakLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const breakIncrementButton = $("#break-increment");
try {
  breakIncrementButton.on("click", () => {
    const breakLength = $("#break-length");
    let lengthValue = Number(breakLength.text());
    if (lengthValue < 60) {
      lengthValue++;
    } else if (lengthValue === 60) {
      throw "lengthValue must be greater than 60";
    }
    breakLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const sessionDecrementButton = $("#session-decrement");
try {
  sessionDecrementButton.on("click", () => {
    const sessionLength = $("#session-length");
    let lengthValue = Number(sessionLength.text());
    if (lengthValue > 1) {
      lengthValue--;
    } else if (lengthValue < 1) {
      throw "lengthValue must not be less than 1";
    }
    sessionLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const sessionIncrementButton = $("#session-increment");
try {
  sessionIncrementButton.on("click", () => {
    const sessionLength = $("#session-length");
    let lengthValue = Number(sessionLength.text());
    lengthValue++;
    sessionLength.text(lengthValue);
  });
} catch (err) {
  console.log(`Error: ${err}`);
}

const defaultSessionLength = 25;
const defaultBreakLength = 5;
const actualSessionLength = $("#session-length").text();
const actualBreakLength = $("#break-length").text();

// default session and break length in seconds
const sessionSeconds = actualSessionLength * 60;
const breakSeconds = actualBreakLength * 60;

let isSessionRunning = true;
let isTimerRunning = false;

const timerElem = $("#time-left");
let timerTextContent = $("#time-left").text();

let totalSeconds = (isSessionRunning) ? sessionSeconds : breakSeconds;

let minutes = parseInt(totalSeconds) / 60;
let seconds = parseInt(totalSeconds) % 60;
let paddedMinutes;
let paddedSeconds;
const initializeTimer = () => {
  // timer value should be 0 at first, so add 1 and then multiply
  if ($("#timer-label").text() === "Session") {
    totalSeconds *= actualSessionLength;
    isSessionRunning = true;
  } else if ($("#timer-label").text === "Break") {
    totalSeconds *= actualBreakLength;
    isSessionRunning = false;
  }

  timerTextContent = `${minutes}:${seconds}`;
  if (minutes < 10) {
    paddedMinutes = minutes.toString().padStart(2, "0");
    timerTextContent = `${Number(paddedMinutes)}:${seconds}`;
  }

  if (seconds < 10) {
    paddedSeconds = seconds.toString().padStart(2, "0");
    timerTextContent = `${minutes}:${Number(paddedSeconds)}`;
  }

  timerElem.text(timerTextContent);
  isTimerRunning = true;
};

const timerCallback = () => {
  if (minutes === 0 && seconds === 0) {
    if ($("#timer-label").text() === "Session") {
      isSessionRunning = false;
    } else if ($("#timer-label").text() === "Break") {
      isSessionRunning = true;
    }
    
    const audioElem = $("#beep");
    audioElem.play();
  }

  totalSeconds--;
  initializeTimer();
};

const timerId = setInterval(timerCallback, 1000);

const startStopButton = $("#start_stop");
startStopButton.on("click", () => {
  if (isTimerRunning) {
    clearInterval(timerId);
  } else {
    setInterval(timerCallback, 1000);
  }
});

const resetButton = $("#reset");
resetButton.on("click", () => {
  // reset app state to default
  clearInterval(timerId);
  const sessionLength = $("#session-length");
  sessionLength.text(defaultSessionLength);

  const breakLength = $("#break-length");
  breakLength.text(defaultBreakLength);

  totalSeconds = sessionSeconds;
  
  minutes = parseInt(totalSeconds) / 60;
  seconds = parseInt(totalSeconds) % 60;
  if (minutes < 10) {
    minutes.toString().padStart(2, "0");
  }

  if (seconds < 10) {
    seconds.toString().padStart(2, "0");
  }

  timerTextContent = `${minutes}:${seconds}`;
  timerElem.text(timerTextContent);
  isTimerRunning = false;
});

HTML:

<!DOCTYPE html>
<html lang="en-us">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <meta name="author" content="Osman Zakir" />
  <title>DragonOsman 25 + 5 Clock Project</title>
  <link rel="stylesheet" href="styles/style.css" />
  <link rel="shortcut icon" href="img/favicon.ico" />
  <link rel="icon" type="image/gif" href="img/animated_favicon1.gif" />
  <link
      rel="stylesheet"
      href="https://use.fontawesome.com/releases/v5.7.0/css/all.css"
      integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ"
      crossorigin="anonymous"
      />
</head>
<body>
  <main>
    <h1>DragonOsman 25 + 5 Clock</h1>
    <div id="break-label" class="break-label">
      Break Length
      <p id="break-length" class="break-length">5</p>
      <button id="break-decrement" class="break-decrement"><i class="fas fa-minus"></i></button>
      <button id="break-increment" class="break-increment"><i class="fas fa-plus"></i></button>
    </div>
    <div id="session-label" class="session-label">
      Session Length
      <p id="session-length" class="session-length">25</p>
      <button id="session-decrement" class="session-decrement"><i class="fas fa-minus"></i></button>
      <button id="session-increment" class="session-increment"><i class="fas fa-plus"></i></button>
    </div>
    <div id="timer" class="timer">
      <!--"timer type" refers to whether it's a session or a break; will change 
        using JavaScript as needed-->
      <p id="timer-label" class="timer-label">Session</p>
      <p id="time-left" class="time-left">25:00</p>
    </div>
    <button id="start_stop" class="start_stop">
      <i class="fas fa-play"></i>
      <i class="fas fa-pause"></i>
    </button>
    <button id="reset" class="reset"><img src="iconfinder_update_678134.ico" alt="reset-icon" /></button>
    <audio src="335909__littlerainyseasons__time-s-up.wav" id="beep"></audio>
  </main>
  <script
    src="https://code.jquery.com/jquery-3.5.1.min.js"
    integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
    crossorigin="anonymous">
  </script>
  <script 
    src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"
    crossorigin="anonymous">
  </script>
  <script src="scripts/script.js"></script>
</body>
</html>

I get the feeling this would be easier in React. After I’m done with the jQuery version I’ll try doing it in React.

Edit: Okay, so what other problems are there in my logic?

You’re still calling initializeTimer in your timerCallback. Which multiplies totalSeconds with actualSessionLength every second. I logged your totalSeconds and at the fourth log, it was at 585530600. Your timer already starts on page load, by the way, which isn’t obvious because it doesn’t get updated in the DOM for whatever reason.

I’m also not sure if the try-catch blocks are the right way to handle invalid inputs like minutes > 60 or <1. I’d rather just not allow updating the input if it gets to an invalid value.

Then, you’re updating the textContent of your time display in initializeTimer (which from the function name I suppose should run only once) instead of your timerCallback (which is the function that runs every second and should take care of updates).

It’s a little difficult for me because I’ve learned jQuery once and have since been trying to wash it out of my brain, but those are some issues I’d address first.

Okay, here’s the updated code:

"use strict";

const breakDecrementButton = $("#break-decrement");
breakDecrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  breakLength.text(lengthValue);
});

const breakIncrementButton = $("#break-increment");
breakIncrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  breakLength.text(lengthValue);
});

const sessionDecrementButton = $("#session-decrement");
sessionDecrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  sessionLength.text(lengthValue);
});

const sessionIncrementButton = $("#session-increment");
sessionIncrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  sessionLength.text(lengthValue);
});

const defaultSessionLength = 25;
const defaultBreakLength = 5;
const actualSessionLength = $("#session-length").text();
const actualBreakLength = $("#break-length").text();

// default session and break length in seconds
const sessionSeconds = actualSessionLength * 60;
const breakSeconds = actualBreakLength * 60;

let isSessionRunning = true;
let isTimerRunning = false;

const timerElem = $("#time-left");
let timerTextContent = $("#time-left").text();

let totalSeconds = (isSessionRunning) ? sessionSeconds : breakSeconds;

let minutes = parseInt(totalSeconds) / 60;
let seconds = parseInt(totalSeconds) % 60;
let paddedMinutes;
let paddedSeconds;

const timerCallback = () => {
  if (minutes === 0 && seconds === 0) {
    if ($("#timer-label").text() === "Session") {
      isSessionRunning = false;
    } else if ($("#timer-label").text() === "Break") {
      isSessionRunning = true;
    }
    
    const audioElem = $("#beep");
    audioElem.play();
  }

  totalSeconds--;

  // timer value should be 0 at first, so add 1 and then multiply
  if ($("#timer-label").text() === "Session") {
    totalSeconds *= actualSessionLength;
    isSessionRunning = true;
  } else if ($("#timer-label").text === "Break") {
    totalSeconds *= actualBreakLength;
    isSessionRunning = false;
  }

  timerTextContent = `${minutes}:${seconds}`;
  if (minutes < 10) {
    paddedMinutes = minutes.toString().padStart(2, "0");
    timerTextContent = `${Number(paddedMinutes)}:${seconds}`;
  }

  if (seconds < 10) {
    paddedSeconds = seconds.toString().padStart(2, "0");
    timerTextContent = `${minutes}:${Number(paddedSeconds)}`;
  }

  timerElem.text(timerTextContent);
  isTimerRunning = true;
};

const timerId = setInterval(timerCallback, 1000);

const startStopButton = $("#start_stop");
startStopButton.on("click", () => {
  if (isTimerRunning) {
    clearInterval(timerId);
  } else {
    setInterval(timerCallback, 1000);
  }
});

const resetButton = $("#reset");
resetButton.on("click", () => {
  // reset app state to default
  clearInterval(timerId);
  const sessionLength = $("#session-length");
  sessionLength.text(defaultSessionLength);

  const breakLength = $("#break-length");
  breakLength.text(defaultBreakLength);

  totalSeconds = sessionSeconds;
  
  minutes = parseInt(totalSeconds) / 60;
  seconds = parseInt(totalSeconds) % 60;
  timerTextContent = `${minutes}:${seconds}`;
  if (minutes < 10) {
    paddedMinutes = minutes.toString().padStart(2, "0");
    timerTextContent = `${Number(paddedMinutes)}:${seconds}`;
  }

  if (seconds < 10) {
    paddedSeconds = seconds.toString().padStart(2, "0");
    timerTextContent = `${minutes}:${Number(paddedSeconds)}`;
  }

  timerTextContent = `${minutes}:${seconds}`;
  timerElem.text(timerTextContent);
  isTimerRunning = false;
});

The problem of the timer display still being “25:0” is still there. How do I fix that? Among other things.

So what am I still doing wrong? Anyone got anything? If possible, someone who’s good with jQuery.

Okay, I have my code like this now:

"use strict";

const breakDecrementButton = $("#break-decrement");
breakDecrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  breakLength.text(lengthValue);
});

const breakIncrementButton = $("#break-increment");
breakIncrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  breakLength.text(lengthValue);
});

const sessionDecrementButton = $("#session-decrement");
sessionDecrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  sessionLength.text(lengthValue);
});

const sessionIncrementButton = $("#session-increment");
sessionIncrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  sessionLength.text(lengthValue);
});

const defaultSessionLength = 25;
const defaultBreakLength = 5;
const actualSessionLength = Number($("#session-length").text());
const actualBreakLength = Number($("#break-length").text());

let isSessionRunning = true;
let isTimerRunning = false;

const timerElem = $("#time-left");
let timerTextContent = timerElem.text();

const today = new Date();
const deadline = new Date();
const deadlineMins = ((isSessionRunning) ? actualSessionLength : actualBreakLength);
deadline.setMinutes(today.getMinutes() + deadlineMins);

const getTimeRemaining = () => {
  const total = Date.parse(deadline.toString()) - Date.parse(today.toString());
  const seconds = Math.floor((total / 1000) % 60);
  const minutes = Math.floor((total / 1000 / 60) % 60);

  return {
    total,
    minutes,
    seconds
  };
};

const timerCallback = () => {
  const timeRemaining = getTimeRemaining();
  if (timeRemaining.total <= 0) {
    clearInterval(timerId);
    const audioElem = $("#beep");
    audioElem.play();
    if ($("#timer-label").text() === "Session") {
      isSessionRunning = false;
      $("#timer-label").text("Break");
    } else if ($("#timer-label").text() === "Break") {
      isSessionRunning = true;
      $("#timer-label").text("Session");
    }
  }

  if ($("#timer-label").text() === "Session") {
    isSessionRunning = true;
  } else if ($("#timer-label").text === "Break") {
    isSessionRunning = false;
  }

  timerTextContent = `${timeRemaining.minutes}:${timeRemaining.seconds}`;
  timeRemaining.total--;
};

let timerId;
timerCallback();

const startStopButton = $("#start_stop");
startStopButton.on("click", () => {
  if (isTimerRunning) {
    clearInterval(timerId);
    isTimerRunning = false;
  } else {
    timerId = setInterval(timerCallback, 1000);
    isTimerRunning = true;
  }
});

const resetButton = $("#reset");
resetButton.on("click", () => {
  // reset app state to default
  clearInterval(timerId);
  const sessionLength = $("#session-length");
  sessionLength.text(defaultSessionLength);

  const breakLength = $("#break-length");
  breakLength.text(defaultBreakLength);

  timerTextContent = `${sessionLength}:00`;
  timerElem.text(timerTextContent);
});

Nothing happens when I click the #start_stop button. And only 17 tests pass. What am I doing wrong?

I went to this site to see how to use the Date object to create a timer.

This is my latest code:

"use strict";

const breakDecrementButton = $("#break-decrement");
breakDecrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  breakLength.text(lengthValue);
});

const breakIncrementButton = $("#break-increment");
breakIncrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  breakLength.text(lengthValue);
});

const sessionDecrementButton = $("#session-decrement");
sessionDecrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  sessionLength.text(lengthValue);
});

const sessionIncrementButton = $("#session-increment");
sessionIncrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  sessionLength.text(lengthValue);
});

const defaultSessionLength = 25;
const defaultBreakLength = 5;

let isSessionRunning = true;

const timerElem = $("#time-left");
let timerTextContent = timerElem.text();

const getTimeRemaining = (deadline, today) => {
  const total = Date.parse(deadline.toString()) - Date.parse(today);
  const seconds = Math.floor((total / 1000) % 60);
  const minutes = Math.floor((total / 1000 / 60) % 60);

  return {
    total,
    minutes,
    seconds
  };
};

let timerId;

const timerCallback = () => {
  timerId = setInterval(() => {
    const today = new Date();
    const deadline = new Date();
    const actualSessionLength = Number($("#session-length").text());
    const actualBreakLength = Number($("#break-length").text());
    const deadlineMins = ((isSessionRunning) ? actualSessionLength : actualBreakLength);
    deadline.setMinutes(today.getMinutes() + deadlineMins);
  
    const timeRemaining = getTimeRemaining(deadline, today);
    if (timeRemaining.total <= 0) {
      clearInterval(timerId);
      const audioElem = $("#beep");
      audioElem.play();
      if ($("#timer-label").text() === "Session") {
        isSessionRunning = false;
        $("#timer-label").text("Break");
      } else if ($("#timer-label").text() === "Break") {
        isSessionRunning = true;
        $("#timer-label").text("Session");
      }
    }

    if ($("#timer-label").text() === "Session") {
      isSessionRunning = true;
    } else if ($("#timer-label").text === "Break") {
      isSessionRunning = false;
    }

    timerTextContent = `${timeRemaining.minutes}:${timeRemaining.seconds}`;

    const stopButton = $("#stop");
    stopButton.on("click", clearInterval(timerId));
  }, 1000);
};

const startButton = $("#start");
startButton.on("click", timerCallback);

const resetButton = $("#reset");
resetButton.on("click", () => {
  // reset app state to default
  clearInterval(timerId);
  const sessionLength = $("#session-length");
  sessionLength.text(defaultSessionLength);

  const breakLength = $("#break-length");
  breakLength.text(defaultBreakLength);

  $("#timer-label").text("Session");

  timerTextContent = `${sessionLength}:00`;
  timerElem.text(timerTextContent);
});

The timer still doesn’t run. I’d really like some help on that: why doesn’t it run? What am I missing?

Also, test 8 for the Content part fails with:

8. I can see an element with corresponding id="time-left". NOTE: Paused or running, the value in this field should always be displayed in mm:ss format (i.e. 25:00).
Cannot read property '1' of null
TypeError: Cannot read property '1' of null

What does this mean and how do I solve it?

Edit: I changed the code again:

"use strict";

const breakDecrementButton = $("#break-decrement");
breakDecrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  breakLength.text(lengthValue);
});

const breakIncrementButton = $("#break-increment");
breakIncrementButton.on("click", () => {
  const breakLength = $("#break-length");
  let lengthValue = Number(breakLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  breakLength.text(lengthValue);
});

const sessionDecrementButton = $("#session-decrement");
sessionDecrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue > 1) {
    lengthValue--;
  }
  sessionLength.text(lengthValue);
});

const sessionIncrementButton = $("#session-increment");
sessionIncrementButton.on("click", () => {
  const sessionLength = $("#session-length");
  let lengthValue = Number(sessionLength.text());
  if (lengthValue < 60) {
    lengthValue++;
  }
  sessionLength.text(lengthValue);
});

const defaultSessionLength = 25;
const defaultBreakLength = 5;

let isSessionRunning = true;

const timerElem = $("#time-left");
let timerTextContent = timerElem.text();
console.log(timerTextContent);

const getTimeRemaining = (deadline, today) => {
  const total = Date.parse(deadline.toString()) - Date.parse(today.toString());
  const seconds = Math.floor((total / 1000) % 60);
  const minutes = Math.floor((total / 1000 / 60) % 60);

  return {
    total,
    minutes,
    seconds
  };
};

const initializeClock = () => {
  const timerId = setInterval(() => {
    const today = new Date();
    const deadline = new Date();
    const actualSessionLength = Number($("#session-length").text());
    const actualBreakLength = Number($("#break-length").text());
    const deadlineMins = ((isSessionRunning) ? actualSessionLength : actualBreakLength);
    deadline.setMinutes(today.getMinutes() + deadlineMins);
  
    const timeRemaining = getTimeRemaining(deadline, today);
    if (timeRemaining.total <= 0) {
      clearInterval(timerId);
      const audioElem = $("#beep");
      audioElem.play();
      if ($("#timer-label").text() === "Session") {
        isSessionRunning = false;
        $("#timer-label").text("Break");
      } else if ($("#timer-label").text() === "Break") {
        isSessionRunning = true;
        $("#timer-label").text("Session");
      }
    }

    if ($("#timer-label").text() === "Session") {
      isSessionRunning = true;
    } else if ($("#timer-label").text === "Break") {
      isSessionRunning = false;
    }

    timerTextContent = `${timeRemaining.minutes}:${timeRemaining.seconds}`;

    const stopButton = $("#stop");
    stopButton.on("click", clearInterval(timerId));

    const resetButton = $("#reset");
    resetButton.on("click", () => {
      // reset app state to default
      clearInterval(timerId);
      const sessionLength = $("#session-length");
      sessionLength.text(defaultSessionLength);

      const breakLength = $("#break-length");
      breakLength.text(defaultBreakLength);

      $("#timer-label").text("Session");

      timerTextContent = `${sessionLength}:00`;
      timerElem.text(timerTextContent);
    });
  }, 1000);
};

const startButton = $("#start");
startButton.on("click", initializeClock);

I made this before:

"use strict";

function getInput() {
  const input = document.getElementById("input").value;
  const deadline = new Date(input);
  return deadline;
}

function leapYear(year) {
  if (year % 4 !== 0) {
    if (year % 400 !== 0 && year % 100 !== 4) {
      return false;
    }
    return false;
  }
  return true;
}

function initializeClock() {
  const deadline = getInput();
  const currentDate = new Date();
  if (deadline < currentDate) {
    const errorPara = document.createElement("p");
    errorPara.textContent = "Sorry; we don't count up from a past event";
    document.querySelector("form").prepend(errorPara);
    errorPara.insertAdjacentHTML("afterend", "<br />");
  } else {
    const timerId = setInterval(() => {
      const now = new Date().getTime();
      const timeDiff = deadline - now;
      let february = 0;
      if (leapYear(deadline.getFullYear())) {
        february = 29;
      } else {
        february = 28;
      }

      const daysInMonth = [31, february, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
      let days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
      let months = Math.floor(days / daysInMonth[deadline.getMonth()]);
      days %= daysInMonth[deadline.getMonth()];

      if (currentDate.getMonth() < deadline.getMonth() && currentDate.getDate() === deadline.getDate()) {
        days = 0;
        const monthDiff = deadline.getMonth() - currentDate.getMonth();
        months = monthDiff;
      } else if (currentDate.getDate() < deadline.getDate() && currentDate.getMonth() === deadline.getMonth()) {
        const daysDiff = deadline.getDate() - currentDate.getDate();
        days = daysDiff;
        months = 0;
      } else if (currentDate.getMonth() < deadline.getMonth() && currentDate.getDate() < deadline.getDate()) {
        const daysDiff = deadline.getDate() - currentDate.getDate();
        const monthsDiff = deadline.getMonth() - currentDate.getMonth();
        days = daysDiff;
        months = monthsDiff;
      }

      const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000);
      document.getElementById("day").textContent = days;
      document.getElementById("month").textContent = months;
      document.getElementById("hour").textContent = hours;
      document.getElementById("minute").textContent = minutes;
      document.getElementById("second").textContent = seconds;
      if (timeDiff < 0) {
        clearInterval(timerId);
        const event = document.getElementById("event");
        document.getElementById("is-it-time").textContent = `${event} is here!`;
        document.getElementById("month").textContent = "0";
        document.getElementById("day").textContent = "0";
        document.getElementById("hour").textContent = "0";
        document.getElementById("minute").textContent = "0";
        document.getElementById("second").textContent = "0";
      }

      const stopBtn = document.getElementById("stop-btn");
      stopBtn.addEventListener("click", function() {
        clearInterval(timerId)
      });
    }, 1000);
  }
}

const startBtn = document.getElementById("start-btn");
startBtn.addEventListener("click", initializeClock);

In that one I kept the code for the button to stop the clock inside same function that I used as the click event handler for the start button. And it worked. So I thought I’d try the same here. But still nothing happens on the 25 + 5 Clock when I click the start button. Why?