I am using the nth-child pseudo-class for an animation that makes the text appear as if it is being typed out. Each time nth-child is used, it is used to delay the animation on that letter.
Considering I am going to be using this for longer sequences of text than just "Sample Text" here, and the delay time goes up in a consistent pattern.
I was wondering if there is a way to optimize this so I don't have to deal with writing pretty much the same thing 30 times?
const typeDiv = document.getElementById("typer");
typeDiv.addEventListener("animationend", whenEnd);
function whenEnd() {
document.getElementById("typer").style.color = "black";
}
#typer span {
animation-name: typing;
animation-duration: 2s;
animation-iteration-count: 1;
}
#typer span:nth-child(1) {
animation-delay: 0s;
}
#typer span:nth-child(2) {
animation-delay: 0.2s;
}
#typer span:nth-child(3) {
animation-delay: 0.4s;
}
#typer span:nth-child(4) {
animation-delay: 0.6s;
}
#typer span:nth-child(5) {
animation-delay: 0.8s;
}
#typer span:nth-child(6) {
animation-delay: 1s;
}
#typer span:nth-child(7) {
animation-delay: 1.2s;
}
#typer span:nth-child(8) {
animation-delay: 1.4s;
}
#typer span:nth-child(9) {
animation-delay: 1.6s;
}
#typer span:nth-child(10) {
animation-delay: 1.8s;
}
#typer span:nth-child(11) {
animation-delay: 2s;
}
@keyframes typing {
0%,
98% {
color: transparent;
}
100% {
color: black;
}
}
<p id="typer" style="color:transparent;">
<span>S</span><span>a</span><span>m</span><span>p</span><span>l</span><span>e</span><span> </span><span>T</span><span>e</span><span>x</span><span>t</span>
</p>
Javascript is a better approach for solving this problem.
My solution below places the first part of each paragraph between <span>
tags, with the remainder of the paragraph outside the tags. At each interval an additional character is moved inside the span
.
If you don’t need the space for the complete paragraph to be reserved before typing starts, you could simplify this even further.
const arrays = document.querySelectorAll('p.type-me')
let intervalID = null
const typeNext = () => {
let stillProcessing = false
arrays.forEach(p => {
if (Number(p.dataset.index) == -1) return // reached the end of this array
stillProcessing = true // there is still more work to do
p.innerHTML = `<span>${p.dataset.text.substring(0, p.dataset.index)}</span>${p.dataset.text.substring(p.dataset.index)}` // re-render the paragraph with more text inside the <span>
p.dataset.index = (Number(p.dataset.index) >= p.dataset.text.length ? -1 : Number(p.dataset.index) + 1) // increment the index or set to -1
})
if (!stillProcessing) clearInterval(intervalID) // stop all further processing
}
// initial setup
arrays.forEach(p => {
p.dataset.text = p.innerHTML.trim()
p.dataset.index = 0
})
// type the next character every 50 milliseconds
intervalID = setInterval(typeNext, 50)
.type-me {
color: #eee;
}
.type-me > span {
color: black;
}
<p class="type-me">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tempor nunc mauris, sit amet placerat tortor lobortis dapibus.
</p>
<p>
Nulla rhoncus aliquam mauris, eu pretium dolor auctor in.
</p>
<p class="type-me">
Nam lectus eros, maximus ac magna vel, congue consequat eros. Fusce id pretium diam. Cras sit amet pharetra ante. Sed quis commodo quam, vel facilisis ipsum. Vestibulum sodales iaculis arcu, et fringilla nisi ullamcorper sed. Donec interdum sit amet est non accumsan. Donec non augue feugiat, fermentum nunc non, convallis est.
</p>