Engaging Website Elements: Add Typing and Backspace Effects with JavaScript

javascript typewritter

On the homepage of my website, I wanted to add an animated effect that appears to backspace some text, and type out new text. Here is a video of the final product:

There are basic JavaScript tutorial snippets that can type things out:

var currentIndex = 0;
var text = 'This is a typing effect!'; /* The text */
var typingSpeed = 50; /* The speed/duration of the effect in milliseconds */

function typeWriter() {
  if (currentIndex < text.length) {
    document.getElementById("output").innerHTML += text.charAt(currentIndex);
    currentIndex++;
    setTimeout(typeWriter, typingSpeed);
  }
}

There are also more robust libraries that could do much more, like TypeIt JS. I shy away from using libraries for small implementations, so I want to write my own vanilla solution. First, I wrapped the dynamic portion in a <span> tag with its own ID:

<h2>Need help with your<br />&nbsp;<span id="dynamicText">website?</span></h2>

I added a line break AND a non-breaking whitespace character to ensure that the changing text used a consistent amount of space. Otherwise, on smaller screen sizes, you could see elements jump as the content changes.

Here is the JavaScript that controls the animation:

  const dynamicText = document.getElementById('dynamicText');
  let originalText = dynamicText.innerText;
  const newTexts = ["app?", "database?", "UI/UX?", "AI?", "API?", "blockchain?", "website?"];
  let textIndex = 0;
  let index = originalText.length;

  function deleteText() {
    dynamicText.innerText = originalText.slice(0, index--);

    if (index >= 0) {
      setTimeout(deleteText, 100); // Adjust the timeout to control the speed of deletion
    } else if (textIndex < newTexts.length) {
      originalText = newTexts[textIndex];
      setTimeout(typeNewText, 500); // Adjust the delay before typing new text
    }
  }

  function typeNewText() {
    if (textIndex < newTexts.length) {
      const newText = newTexts[textIndex];
      index = 0;

      function type() {
        dynamicText.innerText = newText.slice(0, index++);

        if (index <= newText.length) {
          setTimeout(type, 100); // Adjust the timeout to control the speed of typing
        } else {
          textIndex++;
          if (textIndex < newTexts.length) {
            setTimeout(deleteText, 500); // Adjust the delay before deleting the text
          }
        }
      }

      type();
    }
  }

The code snippet begins by declaring a constant variable dynamicText, which stores a reference to an HTML element with the id ‘dynamicText’. This element is where the typing effect will be displayed. Following this, a variable originalText is initialized with the initial text content of the ‘dynamicText’ element. This serves as the starting point for the typing effect.

Next, an array newTexts is defined, containing a list of texts that will be typed out in sequence after the original text is deleted. These texts represent the subsequent messages to be displayed in the typing animation.

Two numerical variables, textIndex and index, are declared to keep track of the current text being typed from the newTexts array and the index within the text being typed, respectively.

The deleteText() function is responsible for deleting the original text character by character until it’s fully removed. It utilizes the setTimeout function to control the speed of deletion. Once the original text is completely deleted, the function triggers the typeNewText() function to start typing the next text from the newTexts array.

Similarly, the typeNewText() function is defined to type out the new text character by character. It also utilizes setTimeout to control the speed of typing. Once the entire new text is typed, the function updates the textIndex to move to the next text in the array and triggers the deleteText() function again to delete the typed text and repeat the process with the next text.

Blinking Cursor

To add a dynamic touch, I incorporated a blinking cursor to the typing effect. The “cursor” itself if a vertical pipe bar character: “|”. I wrapped in a <span> tag and gave it a CSS class cursor.

<h2>Need help with your<br />&nbsp;<span id="dynamicText">website?</span><span class="cursor">|</span></h2>

Making it blink was as simple as adding some CSS rules:

.cursor {
  margin-left: 5px;
  animation: blink 1s steps(1) infinite;
  font-size: .9em;
}

@keyframes blink {
  50% { opacity: 0; }
}

Finally, I remove the cursor when the final word is being typed out:

if(textIndex+1 === newTexts.length){
	var cursorElement = document.querySelector('.cursor');
	if (cursorElement) {
	cursorElement.remove();
}

I add that code to the conditional block responsible for checking if all of the words have been iterated through. The final source looks like this:

const dynamicText = document.getElementById('dynamicText');
let originalText = dynamicText.innerText;
const newTexts = ["app?", "database?", "UI/UX?", "AI?", "API?", "blockchain?", "website?"];
let textIndex = 0;
let index = originalText.length;

function deleteText() {
  dynamicText.innerText = originalText.slice(0, index--);

  if (index >= 0) {
    setTimeout(deleteText, 100); // Adjust the timeout to control the speed of deletion
  } else if (textIndex < newTexts.length) {
    originalText = newTexts[textIndex];
    setTimeout(typeNewText, 500); // Adjust the delay before typing new text
  }
}

function typeNewText() {
  if (textIndex < newTexts.length) {
    const newText = newTexts[textIndex];
    index = 0;

    function type() {
      dynamicText.innerText = newText.slice(0, index++);

      if (index <= newText.length) {
        setTimeout(type, 100); // Adjust the timeout to control the speed of typing

		if(textIndex+1 === newTexts.length){
			var cursorElement = document.querySelector('.cursor');
			if (cursorElement) {
			cursorElement.remove();
		}
        }

      } else {
        textIndex++;
        if (textIndex < newTexts.length) {
          setTimeout(deleteText, 500); // Adjust the delay before deleting the text
        }
      }
    }

    type();
  }
}

window.onload = function() {
  setTimeout(deleteText, 1500);
};