Going back to my jQuery Typewriter posts I’ve had a few requests in the comments, and via email, to make it work with HTML. Previously I thought this would be near impossible, but a bolt of inspiration hit me & this code was born.

Before Reading…

Before reading this tutorial you may want to check out the previous post ‘jQuery Typewriter Revisited‘ which explains the code that this tutorial is based on.

The requests for my jQuery Typewriter to allow HTML within the text have been flooding in, and as I said I had previously though it to be impossible, but it hit me earlier today that it is possible. Let’s take a look at a demo first.

Photo showing the HTML Compatible jQuery Typewriter

At first glance it looks the same as the old jQuery typewriter, but you may have noticed that certain words in the first few lines were different colors. This was done using a <span> tag. If you aren’t familiar with how HTML & the typewriter works you’d be forgiven for thinking: “What’s so special about that?” Well I think it’s difficult to explain, but let’s give it a go.

HTML & jQuery Typewriter… The Problem

The main problem is that because the jQuery typewriter uses substr (or cuts) the text string to only show so much of it, it will always end up cutting the HTML tag too. Now that’s not too bad, all you need is something to tell the script to skip over HTML tags, right? Well no, not exactly.

The problem with just skipping over the opening HTML tag is that your tag is now left open until it reaches the closing tag. Not only can this cause problems with the rest of your page, since the CSS rules applied to that element will now effect everything below the unclosed tag, but it’s wrong from a good coding point of view.

HTML & jQuery Typewriter… The Solution

So, the solution? Well it’s to skip the opening tag as described before, but also to use that opening tag to fake a closing tag until the real closing tag is found. Sounds simple.

If you are having trouble imagining how this would work here is an a little picture to show how it works. If we take the text:

This was a <span class=”evil”>triumph</span>

Below is console output of how the completed typewriter script deals with the HTML as it moves forward each letter.

Console output showing how jQuery Typewriter auto closing an HTML tag works

Now let’s take a peek at the full code, I’ll highlight the differences from the old code & we’ll go through them. Feel free to just copy & paste, but if you want to know how it works, stick around.

//jQuery(function($) { Use this instead of the line below for WP installs
$(function() {
	var ch = 0;
	var item = 0;
	var items = $('#caption li').length;
	var time = 2000;
	var delay = 28;
	var wait = 3000
	var tagOpen = false;

	$('#showCaption').css('width', ($('#caption').width() + 20));

	function tickInterval() {
		if(item < items) {
			var text = $('#caption li:eq('+item+')').html();
			type(text);
			text = null;
			var tick = setTimeout(tickInterval, time);
		} else {
			clearTimeout(tick);
		}
	}

	function type(text) {
		time = delay;
		ch++;
		if(text.substr((ch - 1), 1) == '<') {
			if(text.substr(ch, 1) == '/') {
				tagOpen = false;
			}
			var tag = '';
			while(text.substr((ch - 1), 1) != '>') {
				tag += text.substr((ch - 1), 1);
				ch++;
			}
			ch++;
			tag += '>';
			var html = /\<[a-z]+/i.exec(tag);
			if(html !== null) {
				html = html[0].replace('<', '</') + '>';
				tagOpen = html;
			}
		}
		if(tagOpen !== false) {
			var t = text.substr(0, ch) + tagOpen;
		} else {
			var t = text.substr(0, ch);
		}

		$('#showCaption').html(t);
		if(ch > text.length) {
			item++;
			ch = 0;
			time = wait;
		}
	}

	var tick = setTimeout(tickInterval, time);
});

Now let’s go through the highlighted lines. First we set up a new variable called tagOpen this will keep track of the HTML tag that is currently open.

Now the next highlighted section is all about detecting the HTML tag & making sure it is always closed, if open. First we increase ch by one, since we start at zero and we use it as our length in the substr() function. Next we check if the last letter of that cut text is a waka (<), otherwise known as the opening bracket of an HTML tag. If it is we can assume we’ve found an HTML tag. So we can detect the closing tag we have a check to see if the latter after that is a forward slash (/) used to write a closing tag. If it is we set tagOpen to false since the tag now has it’s real closing tag, so we don’t need our fake one yet..

Next we create a variable to hold the actual tag, then we loop through our text until we find the closing waka (>) of the HTML tag, by using the substr method increasing ch each time we don’t find our waka. As we go we append each letter to the tag variable. Once it finds the closing waka the loop ends. Now because the loop ended when it found the closing waka, tag doesn’t contain it and the ch variable is 1 character lower than it should be. So we add the closing waka to tag and increase ch once more.

Now, HTML tags can contain attributes for example <span class="good">. We don’t want these in our closing tag, so to solve this we use regex to match the opening waka and the first word since that is the predictable format of an HTML tag. This is then stored in the html variable. Our previous code also matches closing tags, and the regex will produce a null result since it won’t match. To prevent this from crashing the code we check to make sure html is not null. If it isn’t we have a match, we replace the opening < with </ to make a closing tag, and tack on the closing waka since that wasn’t given back when the regex matched the tag. We finally set tagOpen to the newly assembled tag so it can survive through as many letters as needed until the real closing tag is found. Remember the code earlier sets tagOpen to false if it found a opening waka & then a forward slash? Well, that’s why.

This final section is to keep the tag closed for each loop after an opening tag has been found, if tagOpen is anything other than false then we must have an open tag so tack the closing tag to the end. If it’s false continue as normal.

There is one caveat to this code, and that is doesn’t work with nested HTML tags, yet. However I am working on it.

That’s It

Phew… I have tried my best to keep the explanation from becoming over complicated, but no matter what I try it still seems to make the mind boggle. Hopefully it makes sense, but if you have any trouble & need me to explain anything in more detail please let me know.