

Build an Infinite Blinking Text Animation With CSS and a Touch of JavaScript
In this new tutorial, we'll learn how to create an infinite blinking/flashing text effect with CSS animations and a bit of JavaScript. Specifically, selected text parts will switch colors after a certain period. After that, the animation will reinitialize.
Our Text Effect
Here’s what we’re going to create. It’s a great addition to a portfolio or company landing page, highlighting brands, names, places, etc.[IFRAME=https://codepen.io/tutsplus/embed/oNVpEZX?default-tab=html%2Cresult&theme-id=dark]Build an Infinite Blinking Text Animation With CSS and a Touch of JavaScript[/IFRAME]
1. Begin With the HTML Markup
Inside a container, we’ll define a wrapper element that will hold some text with well-known brands, like this:
HTML:
<div class="container">
<div class="blinking-wrapper">
Accenture,
Amazon
Apple,
Astrazeneca,
Citigroup,
Coca-Cola Co,
China Merchants Bank,
Disney
Google,
...
</div>
</div>
For each brand (piece of text) we want to animate, we’ll wrap it inside a
span
element and give it the data-number
attribute. The value of the custom attribute will determine the animation order. The smaller the number, the sooner the animation will run. That said, the element with the data-number="1
" attribute value will be animated first, then this with the data-number="2"
attribute value, etc.
Here’s the required markup skeleton
HTML:
<div class="container">
<div class="blinking-wrapper">
Accenture,
Amazon
Apple,
Astrazeneca,
Citigroup,
<span data-number="1">Coca-Cola Co</span>,
China Merchants Bank,
Disney
Google,
IBM,
<span data-number="2">Intel</span>,
JPMorgan Chase,
Mastercard,
McDonalds,
Meta,
NASA,
<span data-number="4">Nestle SA</span>,
Netflix,
Nike,
...
</div>
</div>
2. Add the CSS
The CSS will be pretty basic. All we need is to apply a CSS animation to the
spans
that will change their color from white to a shade of our choice. But note one thing here: the animations will run sequentially when the page loads. At that point, we’ll add the blinking
class to the wrapper and give span
s appropriate delays.In your case, depending on the page layout, you might want to start the blinking effect when the
.blinking-wrapper
element comes into view.
Here are all the styles:
CSS:
/*CUSTOM VARIABLES HERE*/
.container {
max-width: 1600px;
padding: 0 15px;
margin: 0 auto;
}
.blinking-wrapper {
font-size: 50px;
}
.blinking-wrapper.blinking [data-number] {
animation: changeColor 1.5s var(--delay, 0s);
}
@keyframes changeColor {
to {
color: var(--green);
}
}
@media (max-width: 600px) {
.blinking-wrapper {
font-size: 25px;
}
}
3. Apply the JavaScript
As mentioned above, apart from adding theblinking
class to the wrapper element when the page loads, we’ll assign appropriate delays for each span
to determine when their blinking animation will start. The first element won’t have any delay. All the others will fire as soon as the animation of the previous animated element finishes.
Of course, you can set the delays manually or adjust the timings as you wish.
Here’s the required JavaScript code:
JavaScript:
const blinkingWrapper = document.querySelector(".blinking-wrapper");
const animatedEls = blinkingWrapper.querySelectorAll("[data-number]");
const TOGGLE_CLASS = "blinking";
const BASE_DELAY = 1.5;
window.addEventListener("load", function () {
blinkingWrapper.classList.add(TOGGLE_CLASS);
animatedEls.forEach(function (el, index) {
if (index != 0) {
const delay = BASE_DELAY * el.dataset.number - BASE_DELAY;
el.style.setProperty("--delay", `${delay}s`);
}
});
});
Replay Animations
With this code in place, our animations will play as expected for once. But what if we want to replay them in sequence (keeping the desired delays) infinitely? Just using theinfinite
keyword for the animation iteration count won’t work.In such a case, we’ll wait for all animations to finish before reinitializing them using the
animationend
event of the last animated element—the one with the largest data-number
. A quick way to find which element this should be is to give it a unique ID/class manually. Alternatively, we can write some code for retrieving the largest number from all the data-number
attribute values like below:
Show code
JavaScript:
...
function getMaxNumber() {
const numbers = Array.from(animatedEls).map(function (el) {
return el.dataset.number;
});
return Math.max(...numbers);
}
Next, we’ll target the desired element and listen for its
animationend
event.
Show code
JavaScript:
...
blinkingWrapper
.querySelector(`[data-number="${getMaxNumber()}"]`)
.addEventListener("animationend", function () {
//stuff here
});
Inside it, we’ll do three things:
- Remove the blinking class from the wrapper element.
- Force a reflow to that element by calculating its height or anything that makes it reflow without returning anything.
- Add again the
blinking
class to it.
Here’s the required JavaScript code:
JavaScript:
...
blinkingWrapper
.querySelector(`[data-number="${getMaxNumber()}"]`)
.addEventListener("animationend", function () {
blinkingWrapper.classList.remove(TOGGLE_CLASS);
void blinkingWrapper.offsetHeight;
blinkingWrapper.classList.add(TOGGLE_CLASS);
});
An alternative implementation, instead of forcing a reflow that can be expensive depending on your layouts, is to append the class to the wrapper after the shortest possible delay like this—by giving a zero delay, we’ll let the browser decide the shortest delay time:
Show code
JavaScript:
...
blinkingWrapper
.querySelector(`[data-number="${getMaxNumber()}"]`)
.addEventListener("animationend", function () {
blinkingWrapper.classList.remove(TOGGLE_CLASS);
setTimeout(function () {
blinkingWrapper.classList.add(TOGGLE_CLASS);
}, 0);
});
Conclusion
In this tutorial, we built an infinite blinking CSS effect with staggering animations thanks to theanimationend
event. This effect is useful if you want to highlight specific portions of the text like your latest projects, top clients, top skills, etc.
FINAL RESULT
[IFRAME=https://codepen.io/tutsplus/embed/oNVpEZX?default-tab=html%2Cresult&theme-id=dark]Build an Infinite Blinking Text Animation With CSS and a Touch of JavaScript[/IFRAME]