A Better Typewriter Transition In Svelte

Svelte‘s tutorial had a pretty good typewriter transition. Here’s how to make it better with some DOM APIs.

Weiming Wu
3 min readFeb 27, 2021
Svelte’s logo

I recently picked up Svelte by going through its tutorial. This article is about this particular part, on custom JS transitions.

The limitation on that transition is that it only supports elements with one text node, but that’s extremely limiting. Imagine having a bunch of paragraphs that you want transition in with a typewriter — you would need to add transitions to each one and put delays to make one paragraph show after another! Alternatively you could make your text a single string and tinker with CSS to preserve whitespace while sacrificing your HTML structure.

Both are terrible. Let’s fix that.

(Note: I write this half expecting the reader to know how the original transition works. I tried my best to patch up holes in knowledge that I expected, but I’m not a good writer. Don’t stress too much if you can’t follow along — the code is copy-and-paste-able enough if you just want to use it.)

The Enhancements

I had two goals:

  1. I wanted the transition to type out all the content of a page using a single transition, even if it is a series of paragraphs.
  2. I wanted it to work for HTML with more complex structures, such as:
    <p>Go to <a href='https://medium.com/@weimingwu'>this link</a> for more posts by me!</p>

Either one would have completely tripped up the Svelte example, but from getting very familiar with DOM APIs for a feature at work, I knew that it was possible to do.

DOM APIs

Here’s a quick summary of the DOM APIs used:

  • Text nodes are the nodes that contain text.
    (Note that in something like <p>Hello World</p> the text node is not the <p> node. The text node is the implicit node that corresponds to the string “Hello World”. The <p> node is the parent of the text node.
  • DOM nodes in JS have a property, nodeType, which for text nodes is equal to 3 (aka Node.TEXT_NODE).
  • DOM nodes have a textContent property that is assignable. This corresponds to the text displayed inside the node.
  • DOMs are a tree, and DOM nodes have the property childNodes to let us traverse the tree.

How It’s Done:

  1. DOM APIs give us a representation of the DOM tree as a series of JS data structures. Through this DOM tree (starting at the node you’re transitioning in), we can perform a depth-first search to get all the text nodes in display order.
  2. Svelte gives us a way to track progress (the function argument t) with the tick function. The typewriter effect appears when we map t to the characters that we should have displayed on screen. Svelte’s original transition multiplied the text node’s textContent length with t; we’re multiplying the combined textContent length with t. From here on I will call this value progress.
  3. I map the list of nodes to a list of ranges (a number for start and end), which will help me map progress to the node I should edit text on.
  4. While progress is in the node’s range, we will alter the node’stextContent to what we expect it to be at this point in progress, so we need a way to get the node given a value of progress.

The Code

Here is an example of the code, and in action (I make no claims that this code is well-written — I’m just happy that it works!):

In the code, you can see that:

  • Before the animation starts, we set up the transition by making textContentblank for every node.
  • Step 1 is done in getAllTextNodes().
  • Step 2 is done in line 31.
  • Step 3 is done in lines 8 to 14.
  • Step 4 has two parts — the lookup part is done with the getCurrentRange() function, and setting textContent happens in the body of tick.

Conclusion

There you have it! With just a bit of extension from what Svelte gave us, we can make a typewriter transition that works for any HTML. Enjoy writing web pages in Svelte. I know I do!

--

--

Weiming Wu

I learn UI skills that my employer doesn't care about