gconect
gconect

Reputation: 11

Javascript Typewriter effect for multiple lines

I found a nice typewriter effect that prints text when you click the button and it works nicely. However if I want to create multiple buttons that print different texts using this script, it just doesn't work.

Javascript:

<script>
var i = 0;
var txt = 'Text goes here';


var speed = 20;

function typeWriter() {
  if (i < txt.length) {
    document.getElementById("txt").innerHTML += txt.charAt(i);
    i++;
    setTimeout(typeWriter, speed);
  }
}

</script>

HTML:

<a href="#" onclick="typeWriter()">CLICK HERE</a>

<p id="txt"></p>

Upvotes: 0

Views: 1586

Answers (2)

Matt Ellen
Matt Ellen

Reputation: 11592

I offer a slightly more flexible approach:

First of all, create an array of the strings you'd like to have appear with a type writer effect:

const texts = ['abcdefghijklmnopqrstuvwxyz', '1234567890`-=[]#&;./,\!"£$'];

You can see above, that the array has two strings in it. Next loop over the strings. I've done so by calling the forEach method of the array:

texts.forEach(text =>
{
  //work happens here
});

So for each of the strings, create a div with an a, to click on, and a p to write in.

  const div = document.createElement('div');
  const a = document.createElement('a');
  a.setAttribute('href', '#');
  a.innerHTML = 'Type writer go!';
  div.appendChild(a);
  let p = document.createElement('p');
  div.appendChild(p);
  document.body.appendChild(div);

Then for the a add a click listener, that will start the typewriter effect.

  a.addEventListener('click', function(e)
  {
    e.preventDefault();
    nextUpdate(p, 0, text);
  });

As you can see, the click handler calls a function called nextUpdate. This function sets the current text of the p and sets the timeout for the next update.

function nextUpdate(p, index, text)
{
  p.innerHTML = text.substr(0, index);

  if(index < text.length)
  {
    setTimeout(nextUpdate, speed, p, index+1, text);
  }
}

The if there check to see if the string has been completely written. If it has not then the timeout is set up to call nextUpdate again, and the same parameters are passed, but with an increase in the index parameter, which tells the functions how much of the string to read.

const texts = ['abcdefghijklmnopqrstuvwxyz', '1234567890`-=[]#&;./,\!"£$'];
const speed = 150;

//create the document
texts.forEach(text =>
{
  //create the area that the type writer will go
  const div = document.createElement('div');
  const a = document.createElement('a');
  a.setAttribute('href', '#');
  a.innerHTML = 'Type writer go!';
  div.appendChild(a);
  let p = document.createElement('p');
  div.appendChild(p);
  document.body.appendChild(div);
  
  //set the onclick of the link
  a.addEventListener('click', function(e)
  {
    e.preventDefault();
    nextUpdate(p, 0, text);
  });
});

function nextUpdate(p, index, text)
{
  p.innerHTML = text.substr(0, index);
  if(index < text.length)
  {
    setTimeout(nextUpdate, speed, p, index+1, text);
  }
}

Upvotes: 0

Psychemaster
Psychemaster

Reputation: 876

You'll need to modify the function to look more like this (basically, everything that was global should be a parameter of the function instead)

You'll also need to change the function call a bit as a result - see the snippet below.

function typeWriter(messageToShow, targetElement, timeBetween, currentPos = 0) {
  if (currentPos < messageToShow.length) {
    document.getElementById(targetElement).innerHTML += messageToShow.charAt(currentPos);
    currentPos++;
    setTimeout(function() { typeWriter(messageToShow, targetElement, timeBetween, currentPos); }, timeBetween);
  }
}
<button onclick="typeWriter('Hello world', 'demo', 100)">Click me</button>
<button onclick="typeWriter('Other message', 'demo2', 100)">click me two</button>

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

Upvotes: 1

Related Questions