Vanilla JS Typewriter

Continuing the discussion from Typing and deleting letters of a word in the JS:

The previous discussion was mainly between @PaulOB and @Paul_Wilkins I was mostly a spectator then, but things have changed now and JS started making sense. I am redoing what was done in that discussion in small bites.

This was the final version of the codepen by PaulOB →

Some of his additional tries were →




I am doing it in small steps and this is my version of slightly altered code, I am first trying on one president.

const typeText = [
  "Abraham Lincoln",
  "George Washington",
  "Ronald Reagan",
  "John F Kennedy"
];
const myElement = document.getElementById("type");
var timeLoop;
var pos = 0;
var counter = 0;
var increment = 1;

function vanillaTypWriter() {
	var onePresidentSplit = typeText[counter].split("");
	onePresidentSplit.forEach((element) => { 
		myElement.innerHTML = onePresidentSplit.slice(0, pos).join("");
		pos += increment;	
	});	
	timeLoop = setTimeout(myElement.innerHTML, 100);
}

vanillaTypWriter();

I tried to do this based on the information given here where code can also be timed: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
Vanilla_JS

I am faltering at some point may be at syntax or incorrectly applying time delays as animation.

keep in mind that the time parameter on setTimeout is in milliseconds. You are currently telling your script to type a letter every tenth of a second. Also, setTimeout needs to call a function. You’re not passing it a function.

Also, if we’re going to use a variable increment, you don’t want to forEach, you want to for.

1 Like

It can directly be applied to code also. Foreach is working but I am stuck with setTimeout.

well i can tell you for certain that myElement.innerHTML is not executable code (at least, it doesnt DO anything.), nor is it a function.

1 Like

I am finally here →

var onePresidentSplit = typeText[counter].split("");
// setTimeout(code, delay)
onePresidentSplit.forEach((element) => { 
	myElement.innerHTML = myElement.innerHTML + onePresidentSplit[pos];
	pos += increment;	
});

Right, so lets do some debugging. The old school way. Get out a bit of paper and a pencil. Metaphorically, for the moment, but as you code, it can be helpful.

Let’s assume, for the moment, that typeText[counter] is “Abe L” (Yes, i’m shorthanding it.)
What does your code do?

Take “Abe L”, and split it into its individual characters. (Note: This step is technically unnecessary, but we’ll roll with it).
We write down “onePresidentSplit”, and its value: ["A","b","e"," ","L"] on our piece of paper.
Okay. Next line.

For each of the characters in our array:

We now have to include a couple of other definitions:

Otherwise this line doesnt work. So we write down myElement, and <Element with ID 'type'> on our piece of paper, and we write down pos, and 0, on our piece of paper. Now we can go back to the previous line.

we set the innerHTML of the myElement to the current contents of that innerHTML plus onePresidentSplit[pos], which we can identify from our piece of paper that is “A” (because the 0th element of the array is “A”).

Next line.

Again, we need the value of increment. In this case it’s 1. So we write down increment and 1, and we scratch out the 0 next to pos, and replace it with 1.

We’re ending the forEach. So now we repeat this for all elements of the array. At the end of this loop, “Abe L” will be on the end of the myElement innerHTML, pos will be 5.

There has been no delay inserted into this code, so all of this happened at near-instantaneous speed, and the loop really has had no purpose.

There are now two major concerns presenting themselves to me.

ForEach plus Increment.

The example above worked great when increment was 1.
What happens when it’s 2?
Again, lets assume our word is “Abe L”, that our innerHTML starts off empty, and review the code:

onePresidentSplit.forEach((element) => { 
	myElement.innerHTML = myElement.innerHTML + onePresidentSplit[pos];
	pos += increment;	
});
forEach starts. element is "A", pos is 0.
onePresidentSplit[pos] is "A", so it's added to the innerHTML. innerHTML is now "A".
we add 2 to pos. pos is now 2.
Loop ends. Back to the beginning.
forEach starts. element is "b", pos is 2.
onePresidentSplit[pos] is "e", so it's added to the innerHTML. innerHTML is now "Ae"
we add 2 to pos. pos is now 4.
Loop ends. Back to the beginning.
forEach starts. element is "e", pos is 4.
onePresidentSplit[pos] is "L", so it's added to the innerHTML. innerHTML is now "AeL"
we add 2 to pos. pos is now 6.
Loop ends. Back to the beginning.
forEach starts. element is " ", pos is 6.
onePresidentSplit[pos] is an invalid array index. the array only has 5 elements.

Woops.

No Delay

Because the function operates all at once, there’s no delay between letters being added, so from the viewer’s perspective, it instantly appears at full length. This is a problem with the structure’s flow.

You’ve got the inkling that you need to use setTimeout, which is a valid statement, but how?

Rather than looping through the entire array in one go, instead have your ‘code’ (or function, more properly) add exactly 1 character to the string. If there are more characters to be added, set a Timeout to call the function again. Otherwise, you’re done.

2 Likes

Hi there, thanks for helping me. Sir, this is not a finished product I was trying to come one by one. I could not understand few things in your detailed analysis, but for now if you can help me in that limitation, how can I successfully implement setTimeout so that viewer can see the characters as actually typing.

Let’s try this.

Write me a function, typeCharacter, that takes a single string parameter, input.
The function should take the first character from the string, and add it to the screen.
If the string still has characters left, set a timer that:

  1. calls typeCharacter again
  2. does so 500 milliseconds from now,
  3. sends the string, minus its first character, as the argument to typeCharacter. (Hint: Look at the syntax you posted above.)
1 Like

I am very tired, I know you are trying to tell something very significant. will wake up next morning, and will deeply study your post.

Actually, I was carrying this forward from a previous discussion that happened on this platform whose link I shared. I understand that, and I had so much clarity of this, but I was trying something else, in small bites. Later I would have integrated the whole code.

My bad they were already there I didn’t share that with you.

I tried something like this →

var onePresidentSplit = typeText[counter].split("");
onePresidentSplit.forEach((element) => { 
  function overWhelm() {
    myElement.innerHTML = myElement.innerHTML + onePresidentSplit[pos];
    pos += increment;
  }
  setTimeout(overWhelm, 10000);
});

Still, it can create a gap between two strings getting typed.

I also gave this a try, but I know I have some understanding gap →

var onePresidentSplit = typeText[counter].split("");
setTimeout(
onePresidentSplit.forEach((element) => { 
myElement.innerHTML = myElement.innerHTML + onePresidentSplit[pos];
pos += increment;
}),1000);

I still have a difficulty in certain logical flow.

The logical flow is:

Start Function
Remove first character (or first [increment] characters) from string, add it to output.
If the string is not empty:
     Wait X milliseconds
     Call function again with new string
End Function
1 Like

I realized the gap in my understanding, the loop was firing all function in a flash, and those functions were rendering the output in browser in the same instance of time.

I fixed it like this:

var onePresidentSplit = typeText[counter].split("");
for (let step = 0; step < onePresidentSplit.length; step++) {
  function addString() {
    myElement.innerHTML = myElement.innerHTML + onePresidentSplit[pos];    
    pos += increment;       
  }
  setTimeout(addString, step * 100);
}

step * 100 → Key, This will ensure different and yet in Fibonacci order.

This will generate time slots between the functions:
T, 2T, 3T, 4T, 5T.....
(T = 100, in this case).

I am still faltering somewhere in the deletion. Please guide me to identify the mistake. Thank you so much.

Well that’s not Fibonacci anything, that’s just an incrementing value.

        myElement.innerHTML = myElement.innerHTML - onePresidentSplit[pos];   

Javascript doesnt have an anti-concatenation operation. you can’t subtract strings.

1 Like

Ok, Then what JS feature should I used to delete string in decreasing value of POS?

https://www.w3schools.com/jsref/jsref_substring.asp

1 Like

Thanks, There is no other way that actually subtracts just like concantenation?

“That actually subtracts”

What’s the definition of subtract?

What is “tomato” - “onion” ?