Reputation: 177
I have three span tags, and I need to add some text to each in such a way so that it looks like it is being typed out onto the screen. I am using forEach() to loop through each span tag and running a function which is supposed to add each letter one at a time to each span tag consecutively.
However, forEach is adding each letter to all three span tags all at the same time. I only get the desired 'typing' effect on the last span tag once the other two have had all their letters added.
How can I adjust forEach so that it looks like the text is being typed out onto the screen? Please note the text has to be in individual span tags for styling purposes.
let delay = 500;
let currentChar = 1;
let idArr = ["firstSpan", "secondSpan", "thirdSpan"];
let textArr = ["heading ", ": ", "'My Book Title'"];
idArr.forEach((item, index) => {
let text = textArr[index];
function type() {
document.getElementById(item).innerHTML = text.substr(0, currentChar);
currentChar++;
if (currentChar > text.length) {
console.log('text length check running')
currentChar = 1
} else {
setTimeout(type, delay);
}
}
type();
})
.cursor {
color: black;
animation: 1s blink step-end infinite;
}
@keyframes blink {
from, to {
color: transparent;
}
50% {
color: black;
}
}
<span>
<span id="firstSpan"></span>
<span id="secondSpan"></span>
<span id="thirdSpan"></span>
</span>
<span class="cursor">|</span>
Upvotes: 1
Views: 941
Reputation: 16875
(()=>{
const delay = 500;
const elements = ['firstSpan','secondSpan','thirdSpan'].map(
v => document.getElementById(v)
);
const contents = ["heading ", ": ", "'My Book Title'"]
const idx = [0, 0];
const length = [
() => contents[idx[1]].length,
() => contents.length,
];
const doUpdate = () => {
elements[idx[1]].innerText += contents[idx[1]][idx[0]];
if(idx.every((v,i) => ++idx[i] >= length[i]() ? !(idx[i]=0) : 0)) return;
setTimeout(doUpdate, delay);
};
doUpdate();
})();
Upvotes: 1
Reputation: 22320
I can't resist...
const delay = async(ms) => new Promise(r => setTimeout(r, ms))
, spanN = document.querySelectorAll('#sp3 span')
, txtArr = ['heading ', ': ', 'My Book Title' ]
;
async function* LettersSpanGen(aT)
{
let SpLt = aT.reduce((r,c,sp)=>[...r,...[...c].map(lt=>({sp,lt}))],[])
yield* SpLt
}
(async function()
{
for await (let {sp,lt} of LettersSpanGen( txtArr ))
{
spanN[sp].textContent += lt
await delay(500)
}
}
)();
#sp3 span:last-of-type {
--blinkColor : darkblue;
color : var(--blinkColor);
font-weight : bold;
animation : 1s blink step-end infinite;
}
@keyframes blink {
from, to { color: transparent; }
50% { color: var(--blinkColor); }
}
<div id="sp3">
<span></span><span></span><span></span><span>|</span>
</div>
Any questions ?
Upvotes: 1
Reputation: 4870
Your code work fine except that you have to move currentChar
inside the foreach as it belong to item
See example blow
let delay = 500;
let idArr = ["firstSpan", "secondSpan", "thirdSpan"];
let textArr = ["heading ", ": ", "'My Book Title'"];
let currentIndex = 0;
let currentChar = 1;
function type() {
let text = textArr[currentIndex];
var item = idArr[currentIndex];
document.getElementById(item).innerHTML = text.substr(0, currentChar);
currentChar ++;
if(currentChar > text.length) {
console.log('text length check running')
currentChar = 1
currentIndex++;
if (currentIndex < idArr.length)
setTimeout(type, delay);
} else {
setTimeout(type, delay);
}
}
type();
.cursor {
color: black;
animation: 1s blink step-end infinite;
}
@keyframes blink {
from, to {
color: transparent;
}
50% {
color: black;
}
}
<span>
<span id="firstSpan"></span>
<span id="secondSpan"></span>
<span id="thirdSpan"></span>
</span>
<span class="cursor">|</span>
Upvotes: 0