{"id":344497,"date":"2021-07-20T08:03:40","date_gmt":"2021-07-20T15:03:40","guid":{"rendered":"https:\/\/css-tricks.com\/?p=344497"},"modified":"2021-07-20T08:03:43","modified_gmt":"2021-07-20T15:03:43","slug":"typewriter-animation-that-handles-anything-you-throw-at-it","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/typewriter-animation-that-handles-anything-you-throw-at-it\/","title":{"rendered":"Typewriter Animation That Handles Anything You Throw at It"},"content":{"rendered":"\n

I watched Kevin Powell\u2019s video<\/a> where he was able to recreate a nice typewriter-like animation using CSS. It\u2019s neat and you should definitely check it out because there are bonafide CSS tricks in there. I\u2019m sure you\u2019ve seen other CSS attempts at this, including this site\u2019s very own snippet<\/a>.<\/p>\n\n\n\n

Like Kevin, I decided to recreate the animation, but open it up to JavaScript. That way, we have a few extra tools that can make the typing feel a little more natural and even more dynamic. Many of the CSS solutions rely on magic numbers based on the length of the text, but with JavaScript, we can make something that\u2019s capable of taking any text we throw at it.<\/p>\n\n\n\n

So, let\u2019s do that. In this tutorial, I\u2019m going to show that we can animate multiple words just by changing the actual text. No need to modify the code every time you add a new word because JavaScript will do that for you!<\/p>\n\n\n\n\n\n\n

Starting with the text<\/h3>\n\n\n

Let\u2019s start with text. We are using a monospace font to achieve the effect. Why? Because each character or letter occupies an equal amount of horizontal space in a monospaced font, which will come handy when we\u2019ll use the concept of steps()<\/code> while animating the text. Things are much more predictable when we already know the exact width of a character and all characters share the same width.<\/p>\n\n\n\n

We have three elements placed inside a container: one element for the actual text, one for hiding the text, and one for animating the cursor.<\/p>\n\n\n\n

<div class=\"container\">\n  <div class=\"text_hide\"><\/div>\n  <div class=\"text\">Typing Animation<\/div>\n  <div class=\"text_cursor\"><\/div>\n<\/div><\/code><\/pre>\n\n\n\n

We could use ::before<\/code> and ::after<\/code> pseudo-elements here, but they aren\u2019t great for JavaScript. Pseudo-elements are not part of the DOM, but instead are used as extra hooks for styling an element in CSS. It\u2019d be better to work with real elements.<\/p>\n\n\n\n

We\u2019re completely hiding the text behind the .text_hide<\/code> element. That\u2019s key. It\u2019s an empty div that stretches the width of the text and blocks it out until the animation starts\u2014that\u2019s when we start to see the text move out from behind the element.<\/p>\n\n\n\n

<\/figure>\n\n\n\n

In order to cover the entire text element, position the .text_hide<\/code> element on top of the text element having the same height and width as that of the text element. Remember to set the background-color<\/code> of the .text_hide<\/code> element exactly same as that of the background surrounding the text so everything blends in together.<\/p>\n\n\n\n

.container {\n  position: relative;\n}\n.text {\n  font-family: 'Roboto Mono', monospace;\n  font-size: 2rem;\n}\n.text_hide {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  background-color: white;\n}<\/code><\/pre>\n\n\n

The cursor<\/h3>\n\n\n

Next, let\u2019s make that little cursor thing that blinks as the text is being typed. We\u2019ll hold off on the blinking part for just a moment and focus just on the cursor itself.<\/p>\n\n\n\n

Let\u2019s make another element with class .text_cursor<\/code>. The properties are going to be similar to the .text_hide<\/code> element with a minor difference: instead of setting a background-color<\/code>, we will keep the background-color transparent<\/code> (since its technically unnecessary, then add a border to the left edge of the new .text_cursor<\/code> element.<\/p>\n\n\n\n

.text_cursor{\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  background-color: transparent;\n  border-left: 3px solid black;\n}<\/code><\/pre>\n\n\n\n

Now we get something that looks like a cursor that\u2019s ready to move as the text moves:<\/p>\n\n\n\n

<\/figure>\n\n\n

JavaScript animation<\/h3>\n\n\n

Now comes the super fun part\u2014let\u2019s animate this stuff with JavaScript! We\u2019ll start by wrapping everything inside a function called typing_animation()<\/code>.<\/p>\n\n\n\n

function typing_animation(){\n  \/\/ code here\n}\ntyping_animation();<\/code><\/pre>\n\n\n\n

Next task is to store each and every character of text in a single array using the split()<\/code> method. This divides the string into a substring that has only one character and an array containing all the substrings is returned.<\/p>\n\n\n\n

function typing_animation(){\n  let text_element = document.querySelector(\".text\");\n  let text_array = text_element.innerHTML.split(\"\");\n}<\/code><\/pre>\n\n\n\n

For example, if we take \u201cTyping Animation\u201d as a string, then the output is:<\/p>\n\n\n\n

\"(16)<\/figure>\n\n\n\n

We can also determine the total number of characters in the string. In order to get just the words in the string, we replace split(\"\")<\/code> with split(\" \")<\/code>. Note that there is a difference between the two. Here, \" \"<\/code> acts as a separator. Whenever we encounter a single space, it will terminate the substring and store it as an array element. Then the process goes on for the entire string.<\/p>\n\n\n\n

function typing_animation(){\n  let text_element = document.querySelector(\".text\");\n  let text_array = text_element.innerHTML.split(\"\");\n  let all_words = text_element.innerHTML.split(\" \");\n}<\/code><\/pre>\n\n\n\n

For example, for a string \u2018Typing Animation\u2019, the output will be,<\/p>\n\n\n\n

\"(2")<\/figure>\n\n\n\n

Now, let\u2019s calculate the length of the entire string as well as the length of each and every individual word.<\/p>\n\n\n\n

function typing_animation() {\n  let text_element = document.querySelector(\".text\");\n  let text_array = text_element.innerHTML.split(\"\");\n  let all_words = text_element.innerHTML.split(\" \");\n  let text_len = text_array.length;\n\n  const word_len = all_words.map((word) => {\n    return word.length;\n  });\n}<\/code><\/pre>\n\n\n\n

To get the length of the entire string, we have to access the length of the array containing all the characters as individual elements. If we\u2019re talking about the length of a single word, then we can use the map()<\/code> method, which accesses one word at a time from the all_words<\/code> array and then stores the length of the word into a new array called word_len<\/code>. Both the arrays have the same number of elements, but one contains the actual word<\/em> as an element, and the other has the length of the word<\/em> as an element.<\/p>\n\n\n\n

\"\"<\/figure>\n\n\n\n

Now we can animate! We\u2019re using the Web Animation API<\/a> because we\u2019re going with pure JavaScript here\u2014no CSS animations for us in this example.<\/p>\n\n\n\n

First, let\u2019s animate the cursor. It needs to blink on and off infinitely. We need keyframes and animation properties, both of which will be stored in their own JavaScript object. Here are the keyframes:<\/p>\n\n\n\n

document.querySelector(\".text_cursor\").animate([\n  {\n    opacity: 0\n  },\n  {\n    opacity: 0, offset: 0.7\n  },\n  {\n    opacity: 1\n  }\n], cursor_timings);<\/code><\/pre>\n\n\n\n

We have defined three keyframes as objects which are stored in an array. The term offset: 0.7<\/code> simply means that after 70% completion of the animation, the opacity will transition from 0 to 1.<\/p>\n\n\n\n

Now, we have to define the animation properties. For that, let\u2019s create a JavaScript object that holds them together:<\/p>\n\n\n\n

let cursor_timings = {\n  duration: 700, \/\/ milliseconds (0.7 seconds)\n  iterations: Infinity, \/\/ number of times the animation will work\n  easing: 'cubic-bezier(0,.26,.44,.93)' \/\/ timing-function\n}<\/code><\/pre>\n\n\n\n

We can give the animation a name, just like this:<\/p>\n\n\n\n

let animation = document.querySelector(\".text_cursor\").animate([\n  \/\/ keyframes\n], \/\/properties);<\/code><\/pre>\n\n\n\n

Here\u2019s a demo of what we have done so far:<\/p>\n\n\n\n