I am a newbie to JavaScript and trying to make a portfolio for practice. On my portfolio I've been trying to add a typewriter effect on my 'About' section. The idea was to retrieve a String from an array called 'paragraphArray' and display it, one character at a time to a newly created paragraph element for each element in the array in a function which will, for testing purpose, write one character every 0.1 second. The code runs, however everything is printed all at once. Any guidance would be appreciated.
Here is my JS Code:
let paragraphArray = [
"Hi! My name is Zainal Shariff.",
"I graduated from the University of the South Pacific with a bachelors degree, majoring on Computer Science and Information Systems.",
];
let aboutContainer = document.querySelector(".about-container");
document.addEventListener('DOMContentLoaded', typeWriter);
function typeWriter(event) {
for (let i = 0; i < paragraphArray.length; i ) {
let interval1;
let targetParagraph = paragraphArray[i];
let newParagraph = document.createElement("p");
newParagraph.innerHTML = "";
aboutContainer.append(newParagraph)
let j = 0;
if (!interval1) {
interval1 = setInterval(function () {
while (j < targetParagraph.length) {
let character = targetParagraph[j];
printParagraphFromArray(newParagraph, character);
j ;
}
}, 100);
}
aboutContainer.append(document.createElement("br"));
if (i === paragraphArray.length1) {
clearInterval(interval1);
interval1 = null;
}
}
}
function printParagraphFromArray(newParagraph, target) {
newParagraph.innerHTML = newParagraph.innerHTML target;
}
CodePudding user response:
I would put the interval at the top layer of your code -- trying to run time-delayed intervals inside for loops can often result in very confusing code, like what you've written here. I rewrote your code to be a recursive function that just steps through each paragraph, and each character in each paragraph. This removes the confusion of a for loop spawning multiple simultaneous intervals -- the code executes relatively linearly, with only one timeout at a time.
let paragraphArray = [
"Hi! My name is Zainal Shariff.",
"I graduated from the University of the South Pacific with a bachelors degree, majoring on Computer Science and Information Systems.",
];
let aboutContainer = document.querySelector(".about-container");
document.addEventListener('DOMContentLoaded', startParagraph);
let curParagraphIndex = 0;
let curParagraphCharacterIndex = 0;
let curParagraphElement = null;
let curParagraphString = "";
function startParagraph(){
//we're done
if(curParagraphIndex > paragraphArray.length){
return;
}
//otherwise start next paragraph
let newParagraph = document.createElement("p");
newParagraph.innerHTML = "";
aboutContainer.append(newParagraph)
curParagraphElement = newParagraph;
curParagraphCharacterIndex = 0;
curParagraphString = "";
addCharacter();
}
function addCharacter(){
//base case, we're done with this paragraph
if(curParagraphCharacterIndex >= paragraphArray[curParagraphIndex].length){
curParagraphIndex ;
startParagraph();
}else{
//add another character to this paragraph
curParagraphString = paragraphArray[curParagraphIndex][curParagraphCharacterIndex];
curParagraphElement.innerText = curParagraphString;
curParagraphCharacterIndex ;
setTimeout(addCharacter, 100);
}
}
<div >
</div>
CodePudding user response:
It will be easier to make the asynchronous loop a function and have that function called with setTimeout
as long as there are characters to display. Also try to minimize the number of global variables you have: the variables needed to manage the progress of the asynchronous loop should not have to be globals.
Here is a possible implementation:
const paragraphArray = [
"Hi! My name is Zainal Shariff.",
"I graduated from the University of the South Pacific with a bachelors degree, majoring on Computer Science and Information Systems.",
];
const aboutContainer = document.querySelector(".about-container");
document.addEventListener('DOMContentLoaded', typeWriter);
function typeWriter(paraElement, paraIndex=0, charIndex=0) {
if (!charIndex) { // Starting a new paragraph
if (paraIndex >= paragraphArray.length) return; // All done
paraElement = document.createElement("p");
aboutContainer.appendChild(paraElement);
}
paraElement.textContent = paragraphArray[paraIndex].slice(0, charIndex 1);
// Prepare and schedule next run
charIndex = (charIndex 1) % paragraphArray[paraIndex].length;
setTimeout(() => typeWriter(paraElement, paraIndex !charIndex, charIndex), 100);
}
<div ></div>
Note that the initial call to typeWriter
will have the event
object as first argument, but that argument is not used when the second argument is 0 -- which it is in that initial call.