Craft a dynamic countdown timer that blinks using CSS

I am attempting to design a blinking countdown timer that starts at 5 seconds and disappears when it reaches 0.

CSS:

.blinky {
    transition: 1s opacity;
    -moz-transition: 1s opacity;
    -webkit-transition: 1s opacity;
}

HTML:

<div id="countdown" class="blinky">

JS:

const cdStart = 5;
countdown.innerHTML = cdStart;
countdown.style.opacity = 0;
for (var i = cdStart - 1; i > 0; i--) {
    setTimeout(
        (x) => {
            countdownTime.innerHTML = x;
            countdown.classList.remove('blinky');
            countdown.style.opacity = 1;
            countdown.classList.add('blinky');
            countdown.style.opacity = 0;
        },
        1000 * (cdStart - i),
        i
    );
}

The goal is for the timer to display 5, fade out, show 4, fade out, display 3, fade out, show 2, fade out, and finally reveal 1 before disappearing. Each new number should instantly appear without fading back in. To achieve this, the "blinky" class is removed and added accordingly before adjusting the opacity.

However, the current setup only shows 5 and then stops functioning. Removing the opacity manipulation results in a functional countdown from 3 to 1. So I attempted to separate the class removal and addition events:

CSS:

.blinky {
    transition: .9s opacity;
    -moz-transition: .9s opacity;
    -webkit-transition: .9s opacity;
}

JS:

const cdStart = 5;
countdownTime.innerHTML = cdStart;
countdown.style.opacity = 0;
for (var i = cdStart - 1; i > 0; i--) {
    setTimeout(
        (x) => {
            countdownTime.innerHTML = x;
            countdown.classList.remove('blinky');
            countdown.style.opacity = '';
        },
        1000 * (cdStart - i),
        i
    );
    setTimeout(() => {
        countdown.classList.add('blinky');
        countdown.style.opacity = 0;
    }, 1000 * (cdStart - i) + 100);
}

With this attempt, I observed 5 fading out and then a delay before 1 appeared and faded away.

Is there a more reliable method to achieve the desired functionality here?

Answer №1

Utilizing CSS Animations allows you to achieve a subtle fade-out effect every time the counter changes.

Include an animation with 5 iterations and monitor the animationiteration and animationend events. These events trigger each time the animation loops or finishes playing.

Update the count and adjust the text content of the countdown in both event handlers. Delete the .blinky class once the animation completes.

const countdown = document.querySelector('#countdown');
let count = 5;

function updateCount() {
  countdown.textContent = --count;
}

function removeBlinky() {
  countdown.classList.remove('blinky');
}

countdown.textContent = count;
countdown.addEventListener('animationiteration', updateCount);
countdown.addEventListener('animationend', () => {
  updateCount();
  removeBlinky();
});
@keyframes blink-out {
  0%, 25% {
    opacity: 1;
  }
  
  100% {
    opacity: 0;
  }
}

#countdown {
  font-size: 48px;
}

.blinky {
  animation: blink-out 1s ease-out forwards 5;
}
<div id="countdown" class="blinky"></div>

Answer №2

It appears that I have understood the task correctly:

let timer = document.getElementById('countdown');
let secondsLeft = 5;
timer.innerText = secondsLeft;
timer.classList.add('countdown-animation');
const interval = setInterval(()=>{
secondsLeft--;
timer.innerText = secondsLeft;
if(!secondsLeft)
   stopCountdown();
},1000)
function stopCountdown(){
 timer.classList.remove('countdown-animation');
 clearInterval(interval);
}
.timer{
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
}
.countdown-animation{
  animation:1s fade-out ease;
  animation-iteration-count:infinite;
  animation-fill-mode:forwards;
}
#countdown{
font-size:10rem;
}
@keyframes fade-out{
from{opacity:1}
99%{opacity:0}
to{opacity:1}
}
<body>
<div class="timer">
<span id="countdown"></span>
</div>
</body>

Answer №3

Here is a demonstration utilizing async functions and css transitions to manage fading effects.

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
const findElement = str => document.querySelector(str);
const button = findElement("button");
const numDisplay = findElement(".number");
const DELAY_TIME = 1000;

async function modifyNumber(newNum) {
    numDisplay.classList.add("fade");
    await wait(DELAY_TIME);
    numDisplay.textContent = newNum;
    numDisplay.classList.remove("fade");
    await wait(DELAY_TIME);

}

async function countdown(startNum) {
  const INITIAL_VALUE = startNum;
  numDisplay.textContent = INITIAL_VALUE;
  await wait(DELAY_TIME);
  for (let countVal = INITIAL_VALUE - 1; countVal >= 0; countVal--) {
    await modifyNumber(countVal);
  }
}

button.addEventListener("click", async () => {
  button.disabled = true;
  await countdown(5);
  button.disabled = false;
});
.timer {
  display: grid;
  place-items: center;

  width: 100px;
  height: 150px;
  border-radius: 20px;
  border: 5px solid lightgreen;
}

.number {
  font-size: 5rem;
  font-family: monospace;
  transition: opacity 1s;
}

.number.fade {
  opacity: 0;
}
<div class="timer">
  <span class="number"></span>
</div>
<br />
<button>count from 5!</button>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Keep your data safe and protected within a Node CLI application

Currently developing a NodeJS command-line application that interacts with an API to provide data to users. To access the public API, users need an API token. The CLI will be globally installed on users' machines using npm i -g super-cool-api-cli. Up ...

Can I increase the top margin by more than mt-5?

Seeking help to align elements on my website. I've applied mt-5 in the HTML, yet require additional margin space. Any suggestions on how to achieve this? ...

I must first log a variable using console.log, then execute a function on the same line, followed by logging the variable again

Essentially, I have a variable called c1 that is assigned a random hexadecimal value. After printing this to the console, I want to print another hex value without creating a new variable (because I'm feeling lazy). Instead, I intend to achieve this t ...

Ensure that the submit button triggers the display of results with each click

My project involves two navigation bars, each with its own table displayed upon page load. Additionally, there is a search bar used to display search results in another table. The issue I'm encountering is that when I click the submit button once, th ...

Exploring the best practices for loading state from local storage in VueJS/NuxtJS by leveraging the "useLocalStorage" utility

When attempting to utilize useLocalStorage from pinia, I am encountering an issue where the data in the store's state is not fetched from local storage and instead defaults to the default value. Below is the code snippet from my store method: import ...

saving numeric values and text to a document using node.js

In my node.js application, I am working with files to read and write numbers and strings. Currently, I am using fs.writeFileSync(myPath, value); where the value can be either a number or a string. When I try to read the file using fs.readFileSync(myPa ...

Attempting to move elements into an array for storage in the local storage

Is there a way to properly add elements to an array and store it in localstorage? Here's the code snippet I've been working with: const handleSelectLayouts = (layout) => { const layoutsArray = []; layoutsArray.includes(layout) ...

How are jQuery.ajax and XMLHttpRequest different from each other?

My goal is to fetch and run the script contained in a file named "example.js" using an AJAX request. Suppose the content of example.js looks like this: const greetings = { hello: "Hello", goodBye: "Good bye" } console.log(greetings.hello) In anot ...

Ways to launch numerous URLs in express.js

I am currently developing a service similar to a URL shortener. While a typical URL shortener redirects the user to one page, my service needs to open multiple URLs simultaneously. When a user clicks on a link from my website, I want it to open multiple l ...

Is it possible to execute in a specific context using npm?

I am seeking to execute npm scripts that are executable by VuePress. For instance, I have VuePress installed and would like to run the command vuepress eject. Although I can access vuepress in my scripts, there is no specific script for eject: "scr ...

What is the best way to make the background color of section 1/2 cover the entire screen?

Is there a way to make the background color of section 1/2 fill the entire screen? Check out this link for more information. <div id="sections"> <div class="section one"> <a href="#section two"> <i class="fa fa-an ...

The initial attempt to use autocomplete with Jquery UI is not functioning as expected upon entry

I'm facing a frustrating issue that's driving me crazy. I'm not an expert in javascript, but I believe the solution is simple. I'm using jQuery UI autocomplete with data retrieved from Ajax. The problem is, I only get the desired resul ...

Tips for formatting JSON using jQuery

Imagine my JSON data with 3 different sets of information: [ { "Pair":"", "Id":"8ca2df56-2523-4bc3-a648-61ec4debcaaf", "PubDate":"/Date(1463775846000)/", "Provider":null, "Market":"" }, { "Pair":"", ...

meteor Error: IDENTIFIER is missing

I recently started following the Angular-Meteor framework tutorial () but I encountered an error towards the end that I'm struggling to resolve. Despite my efforts in looking for a solution, my limited understanding of the framework seems to be hinder ...

Tips for CSS: Preventing onhover animation from resetting with each hover

I've created an on-hover CSS animation that smoothly transitions between images. However, I encountered a lagging issue when the user quickly hovers over SECTION ONE and SECTION TWO before the animation ends, causing the animation to restart and lag. ...

Unable to access property 'map' of undefined - having trouble mapping data retrieved from Axios request

When working with React, I have encountered an issue while trying to fetch data from an API I created. The console correctly displays the response, which is a list of user names. However, the mapping process is not functioning as expected. Any insights or ...

Error Connecting to Database with Node.JS MySQL Module - ECONNRESET Issue

Attempting to establish a connection with my database using the mysql module has been quite the challenge. Each time I try, an error seems to pop up: read eCONNRESET There is problem. (The final part is from my console log, as seen below.) I've ruled ...

AngularJS: Display the last four characters of a string and substitute the rest with 'X'

I am attempting to change the characters with X and make it look something like this XXXXXT123 This is what I have tried: var sno = 'TEST123'; alert(sno.slice(0,3).replaceWith('X')); However, I encountered an error in the console ...

Adjust the sizing of all fonts following the switch in font styles

Seeking help for adjusting font-size across a responsive web page design using different font families. font-family:Cambria, "Hoefler Text", "Liberation Serif", Times, "Times New Roman", serif; To achieve responsiveness, various media queries were applie ...

Guide to aligning a container to the left in Bootstrap 5

I have a grid container in Bootstrap 5 and I'd like to align it to the left on my webpage. <link href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3f5d50504b4c4b4b54e42f56b61a0acf40"&g ...