Function using setTimeout throws on subsequent calls

Viewed 103

I'm trying something easy, a typewriting effect:

var i = 0;
var txt = "eco eco"
var speed = 1;
function typeWriter(idtxt) {
  if (i < txt.length) {
    document.getElementById(idtxt).innerHTML += txt.charAt(i);
    i++;
    setTimeout(typeWriter, speed);
  }
};
<button class="remo-btn" onclick="typeWriter('demo')">
<p id="demo"></p>

When I hardcode the id, the code works as intended:

document.getElementById("demo").innerHTML += txt.charAt(i);

But when I pass the id like this:

document.getElementById(idtxt).innerHTML += txt.charAt(i);

It only returns the first letter, in this case, "e" (from echo).

Can somebody help me?

4 Answers

You forgot to pass in idtxt while calling typeWriter in the setTimeout:

var i = 0;
var txt = "eco eco"
var speed = 1000;
function typeWriter(idtxt) {
  if (i < txt.length) {
    document.getElementById(idtxt).innerHTML += txt.charAt(i);
    i++;
    setTimeout(() => typeWriter(idtxt), speed);
  }
};
<button class="remo-btn" onclick="typeWriter('demo')">
<p id="demo"></p>

This is happening because the idtxt variable is out of scope after the first call to typeWriter. You can pass the parameter to subsequent calls to typeWriter by passing it as the first argument of setTimeout. Like so:

setTimeout(typeWriter, speed, idtxt);

You can read more about setTimeout here: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout

And you can learn more about why this is necessary here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

var i = 0;
var txt = "eco eco"
var speed = 1000;
function typeWriter(idtxt) {
  if (i < txt.length) {
    document.getElementById(idtxt).innerHTML += txt.charAt(i);
    i++;
    setTimeout(typeWriter, speed, idtxt);
  }
};
<button class="remo-btn" onclick="typeWriter('demo')">
<p id="demo"></p>

Also, you probably meant for speed to equal 1000 (1 second). The delay of setTimeout is measured in milliseconds.

ES6 offer other way for that.

for example with Promise management:

const delay = ms => new Promise(r => setTimeout(r, ms))
  ,    txt  = 'Hello, world!'
  ;
async function typeWriter(idtxt)
  {
  const elm = document.getElementById(idtxt)
  for await (let letter of txt)
    {
    elm.textContent += letter
    await delay(500)
    }
  }
<button class="remo-btn" onclick="typeWriter('demo')">remo-btn</button>

<p id="demo"></p>

change setTimeout(typeWriter, speed ); to setTimeout(typeWriter, speed, idtxt );
you are missing to send idtxt argument

PS cahnge also var speed = 1000; 1s == 1000ms

var i = 0;
var txt = "eco eco"
var speed = 1000;
function typeWriter(idtxt) {
  if (i < txt.length) {
    document.getElementById(idtxt).innerHTML += txt.charAt(i);
    i++;
    setTimeout(typeWriter, speed, idtxt );
  }
};
<button class="remo-btn" onclick="typeWriter('demo')">
<p id="demo"></p>

Related