David Walsh Blog

Create a Dynamic Table of Contents Using MooTools 1.2

You’ve probably noticed that I shy away from writing really long articles. Here are a few reasons why:

Say I did put together a lengthy article — I’d want to automate a table of contents, right? Well, here’s how I would do it.

The XHTML

<h1>Article Title</h1><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>
<h2>Article Title 2 (1)</h2><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>
<h3>Article Title 3 (1)</h3><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>
<h3>Article Title 3 (2)</h3><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>
<h3>Article Title 3 (3)</h3><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>
<h2>Article Title 2 (2)</h2><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>
<h2>Article Title 2 (3)</h2><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>
<h3>Article Title 3 (4)</h3><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.....</p>

As you can see, the above content is really just rubbish for our example. Note the order of the “h” tags.

The CSS

#toc		{ float:right; border:1px solid #fc0; background:#fffea1; padding:10px; font-family:tahoma, arial; margin:0 0 20px 20px; }
#toc a 		{ display:block; margin:3px; }
#toc .h1	{ color:#090; }
#toc .h2	{ padding:0 0 0 10px; font-size:11px; }
#toc .h3	{ color:#f00; font-size:10px; padding:0 0 0 30px; }

The above CSS applies only to the table of contents DIV we’ll be building. The JavaScript will add the “h{x}” class to the generated link to that section. Note that the TOC will start hidden.

The MooTools JavaScript


//once the dom is set...
window.addEvent('domready', function() {

	//initial vars
	var finders = ['h1','h2','h3','h4','h5','h6'];
	var matches = [];

	//find the h1, which is the article title
	$('article-area').getElements('*').each(function(el,i) {

		//do we want this?
		if(finders.contains(el.get('tag')))
		{
			//create anchor
			var anchor = new Element('a', {
				'class': el.get('tag'),
				'text': el.get('text'),
				'href': 'javascript:;'
			});

			//click event
			anchor.addEvent('click', function() {
				var go = new Fx.Scroll(window).toElement(el);
			});

			//add into our matches array
			matches.include(anchor);
		}

	});

	//should we show the toc?
	if(matches.length)
	{
		//create toc div, inject
		var toc = new Element('div', {
			'id': 'toc',
			'html': '<strong>Table of Contents</strong><br />'
		}).inject('article-area','before');

		//inject the matches
		matches.each(function(el) {
			el.inject(toc);
		});
	}

});

There’s a lot going on here so let me break it down. First, I initialize a few vars. The most important is “finders,” which allows us to set the tags to be included in the table of contents. Next, we grab all elements inside my designated “article-area” element. If the element is in our “finders” list, we create an anchor for it, attach smooth scrolling to the anchor’s “click” event, and store the anchor in our “matches” array. In the end, if we find any matches, we create the DIV and float it to the right. If not, the TOC never displays. Sweet!

Notes