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.
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:
- 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.
- 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 (akaNode.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:
- 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.
- Svelte gives us a way to track progress (the function argument
t
) with thetick
function. The typewriter effect appears when we mapt
to the characters that we should have displayed on screen. Svelte’s original transition multiplied the text node’stextContent
length witht
; we’re multiplying the combinedtextContent
length witht
. From here on I will call this valueprogress
. - 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. - 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 ofprogress
.
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
textContent
blank 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 settingtextContent
happens in the body oftick
.
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!