[HELP] typing effect

I’m trying to learn js, it’s good, I’m enjoying it, it is perplexed and it makes my brain hurt…

Here is the pen:

How am I going to add the delay to the letters?

Whilst it’s a good idea to learn JS, you can use CSS to produce a typing effect. Something along the lines of:

<!doctype html>
<html lang="en-GB">
<head>
<meta charset="utf-8">
<style>
html {
  display: table;
  height: 100%;
  width: 100%;
}
body {
  display: table-cell;
  vertical-align: middle;
  margin: 2em auto;
  background: black;
}
.typetext {
  font-family: Courier, monospace;
  font-size: 1.12em;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
}
.typetext p {
  width: 100%;
  color: lime;
  white-space: nowrap;
  overflow: hidden;
  animation: type 2s steps(60, end);
  margin: 1em 0;
}
.typetext p + p {
  width: 0;
  animation: type2 6s steps(60, end) forwards;
}

.typetext p:nth-child(2) { animation-delay: 0s; }
.typetext p:nth-child(3) { animation-delay: 3s; }
.typetext p:nth-child(4) { animation-delay: 6s; }
.typetext p:nth-child(5) { animation-delay: 9s; }
.typetext p:nth-child(6) { animation-delay: 12s; }
.typetext p:nth-child(7) { animation-delay: 15s; }
.typetext p:nth-child(8) { animation-delay: 18s; }
.typetext p:nth-child(9) { animation-delay: 21s; }

@keyframes type { 
  from { width: 0; } 
} 
@keyframes type2 {
    0% { width: 0; }
   50% { width: 0; }
  100% { width: 100%; } 
}
</style>
</head>
<body>
<div class="typetext">
  <p>This website is dead.</p>
  <p>It is an ex-website.</p>
  <p>It has passed on.</p>
  <p>This website is no more.</p>
  <p>It has ceased to be.</p>
  <p>It has expired and gone to meet its maker ...</p>
  <p>... and all despite its beautiful plumage.</p>
</div>
</body>
</html>

Edit: not all my own work :slight_smile:

4 Likes

Here’s how I did it using your setup:

const texts = ["Hello World!","This is a test."];
const box = document.getElementById("box");
const h1 = box.getElementsByTagName("h1");
let i = 0;

function type(word,letter) {
	h1[0].innerHTML += texts[word][letter];
	letter = (letter + 1) % texts[word].length
	if (letter == 0) {
		word++;
		h1[0].innerHTML += "<br>";
	}
	if (word < texts.length) {
		setTimeout(type,500,word,letter);
	}
}

window.onload = function() { type(0,0) }

DISCLAIMER: I know there are ‘better’ and ‘more ECMAScript’ ways of doing this; I am demonstrating to a person learning Javascript how to use setTimeout, without diving into the world of Promises and Async functions. (also, cant find my Codepen login. go me.)

2 Likes

And for a bit more ‘human type-y’ effect, I did this:

	setTimeout(type,100+Math.floor(Math.random()*400),word,letter);

(Something extra to chew your brain on; what am i doing, and why.)

2 Likes

Wait. When I was thinking about the typing effect, it should loop through the word then output it. I saw some examples of it and they didn’t even use any kind of loop, they used setTimeout and setInterval. Don’t they run forever?

setTimeout executes exactly once. (Note that I put setTimeout inside the function, calling the same function; so it effectively ‘loops’, This is a (delayed, broken?) form of Recursive Function Calling.)
setInterval will execute every X milliseconds.

That’s the difference between them.

Oh. It’s still not clear. But that’s my own problem. Anyways, Thanks!

Well if something’s unclear, then it’s best to ask!

I can pair down the code a bit to demonstrate the setTimeout ‘loop’.
First, let’s demonstrate a Recursive function;

function loopme(x) {
  if(x == 0) { 
     console.log("Done"); 
     return;
  }
  console.log(x);
  loopme(x-1);
  console.log(x);
}

For example, if i put into the console loopme(5), the code begins with x=5.
if(x == 0) is false, because x is 5.
console.log(x) will print 5 in the log.
loopme(x-1) will call the function again, but this time with x=4.
It will then wait for that execution of the function to finish, and then move on.
console.log(x) will print 5 in the log again.

But that ‘it will wait’ line is important; because it means that the code will dive down and down and down until it hits the stop condition. In this case, that x == 0. When x == 0, we don’t call the function again, we return. At that point, we start going back up the chain.
What will be in the console at the end?

SetTimeout
SetTimeout says “After X milliseconds, execute this function.”

setTimeout(function() { console.log("Timer done!"); },5000);

This script waits 5 seconds (5000 milliseconds), and then executes the function. The function writes a line to the console.

So how do I combine the two? And what happens differently?

function loopme(x) {
  if(x == 0) { 
     console.log("Done"); 
     return;
  }
  console.log(x);
  setTimeout(loopme,5000,x-1);
  console.log(x);
}

Why is this one different? Well, we’ve delayed the inner execution; but setTimeout is what is called asynchronous.
Instead of waiting for setTimeout to do its execution, the script immediately continues its execution, and then completely separately, when the timer expires, its function fires.

So this is why I couldnt use a forEach and sleep; because setTimeout is asynchronous, what I had to do instead was say “write a single letter. now if there are more letters, start a timer, and when it finishes, run this function again targetting the next letter.”

2 Likes

Do we need to manually break the timeout or it will automatically stop once the condition failed?

Well, i’m not quite sure what you mean by ‘break the timeout’.

In this one:
setTimeout(function() { console.log("Timer done!"); },5000);
It runs once. Exactly 5000 milliseconds after the processor gets to this line (okay, maybe a millisecond or two later, as it processes the command before setting the timer), it will execute the function. And then it’s done. Finished.

In THIS one:

function loopme(x) {
  if(x == 0) { 
     console.log("Done"); 
     return;
  }
  console.log(x);
  setTimeout(loopme,5000,x-1);
  console.log(x);
}

Consider what happens if we say loopme(2) somewhere else in the code.

NOTE: For this, I will assume each line of code takes 1 millisecond, and put the timing of events on the left.

    1: Function Starts; x = 2;
    2:  if(x == 0) {  //FALSE. Skip to end of declaration.
    3:  console.log(x); // Write "2" to the console.
    4:  setTimeout(loopme,5000,x-1); //Start a timer for 5000 milliseconds.
    5:  console.log(x); // Write "2" to the console.
    6:  return undefined //Implied, this ends the current execution of this function. 
 5004: Timer from "4" expires. Function Starts; x = 1 //Because we passed x-1 back at "4". 
 5005:  if(x == 0) {  //FALSE. Skip to end of declaration.
 5006:  console.log(x); // Write "1" to the console. 
 5007:  setTimeout(loopme,5000,x-1); //Start a timer for 5000 milliseconds.
 5008:  console.log(x); // Write "1" to the console.
 5009:  return undefined //Implied, this ends the current execution of this function.
10007: Timer from 5007 expires. Function Starts; x = 0.
10008:  if(x == 0) {  //TRUE, continue
10009:  console.log("Done"); // Write "Done" to the console.
10010:  return; //Return undefined.

Note that the last function call didn’t start a new timer. So once it’s done… the engine isnt running any more timers, it’s finished processing all the functions that were in its stack; it’s finished. (It’s still listening, of course, because javascript is always listening for event triggers or new commands from the console, etc.)

EDIT: I can do math. 4+5000 = 5001! yeah…

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.