Automatic Page Contents
It's been so long since I last posted a tutorial here that I'm afraid everyone might have forgotten about the place. For the past few months, there has been a little "Page Contents" menu at the top-right corner of some of the pages on this site — actually, any page that has more than one <h2> elements in the main content area. In this entry, I'd like to demonstrate how to create an automatic page contents list using jQuery.
The Plugin Option
Before we begin, I should let you know that there are a number of plugins that do what I'm about to show you. If you want to go the plug-in route, take a look at one of these:
- jqTOC by Dave G.
- Page Contents Menu (.js file) by Joel Birch
But if you want to see how to do this sort of thing on your own, read on...
Let's Begin
Here is what we need to do:
- Find out if there is more than one
<h2>in the main content area of the page (on this site, the home page and most listing pages will have more than one). If so, we start building the table of contents... - Create a new container
<div>for the table of contents and insert a heading and an empty div inside. - Copy the
<h2>elements and the<a>elements inside them. - Change the attributes of the copied links.
- Insert each copied
<h2>along with its contained link into the page-contents<div>.

When we're finished, we should have something that looks like the image to the right:
Create the container
Okay, so our first step is to create the container. We'll wrap our code in a $(document).ready(), as we do nearly every time, so that the script loads when the DOM (but not necessarily image, etc.) has loaded. Then we make sure that we're dealing with more than one <h2> and, if so, start building:
Okay, maybe a little explanation is warranted here. The first line inside our if condition (line 3) creates a new <div> element with an id of "page-contents." Line 4 inserts an <h3> at the beginning of that new <div> and line 5 inserts an empty <div> at the end of the page-contents <div>. Finally, line 5 inserts the whole group of newly created elements at the beginning of the <body>.
Copy the headings
The next part involves copying all of the <h2> elements, and while we're at it, changing the href of the copied <a> elements to point to the original <h2>s. How do we know where to point them? Each of the original <h2>s has an id associated with it. We just take the id, prepend a "#", and attach it to the href:
-
if ( $('#content h2').length> 1) {
-
// ... etc. ...
-
-
var $this = $(this);
-
var thisId = this.id;
-
$this
-
'href': '#' + thisId
-
})
-
});
-
}
-
});
We use a .each() method here so that we can apply a number of things to each <h2> in turn. Inside this method, we start by declaring a couple variables—one for $(this), because we use it twice, and one for each id, because we use it to set the hrefs.
So, for each <h2>, we make a copy using .clone() and find the <a> inside of it. Then we set each link's title and href attribute.
When we're finished with the links, we back up to the cloned <h2> elements by using .end(). We want to ensure that we don't duplicate the id along with the element, so we prepend "pc-" to it, keeping each one unique.
When all is ready, we append each <h2> to the <div>inside the page-contents container.
A Little Extra Flair
Now, for an optional addition to our script, we can hide the page-contents <div> in a style sheet (by setting it to display: none). Then we attach a click handler to the page-contents heading with a .toggleClass() to change the background image and a .slideToggle() to show and hide the same-page links.
Here is the complete script, with the additional click handler at the end:
-
if ( $('#content h2').length> 1) {
-
$('<div id="page-contents"></div>')
-
-
var $this = $(this);
-
var thisId = this.id;
-
$this
-
'href': '#' + thisId
-
})
-
});
-
-
});
-
}
-
});
If you want to see it in action, just go to the Learning jQuery home page and look in the top-right corner of the page.



June 25th, 2007 at 12:39 pm
Great post, thanks! It's always nice to see how fellow codemen-in-arms use jQuery.
June 26th, 2007 at 4:04 am
Very nice! Keep up the posting of good articles!
June 27th, 2007 at 6:33 pm
Hi Karl,
well done. While reading your post I realized that it's possible for a link to directly jump to an id (which makes perfectly sense). I was still in the good old
<a name="myAnchor">mode ...Modification is needed if your
<h2>s just don't have an id. In that case we have to generate + add them as well.June 28th, 2007 at 11:31 am
Hi Ralf,
Yeah, I think IDs are definitely the way to go. There might be an older browser (NN4?) that doesn't support jumping to IDs, so be sure to test it first if you need to support any dinosaurs.
Thanks for pointing out the need to modify things if the
<h2>s don't already have anid. Also, if the<h2>s don't already have an<a>inside of them, then the.clone()method is not really the best choice. For that situation, it makes sense to create a new<a>for each<h2>. So, inside the.each()we'd do something like this:$('<a></a>').attr('href',$(this).text())and append it to the page content's child<div>.July 12th, 2007 at 2:52 am
guess i'm not good with this
July 17th, 2007 at 12:52 am
hey... thanks for sharing this suff
September 10th, 2007 at 4:22 am
[...] Automatic Page Contents In this entry, I’d like to demonstrate how to create an automatic page contents list using jQuery. (tags: jquery) [...]
October 25th, 2007 at 4:29 am
Hi karl,
Nice stuff! But i have one q. How can i get the content - in the sense suppose I have top topics in my site & that content I want to get on other page to publish some where else. Is there anything for this?
Please help me out.
Thanks for listening me.
March 7th, 2008 at 3:45 pm
Real neat!
March 17th, 2008 at 4:45 pm
Excellent post ! I'm subscribing to your feed so I can make sure I catch more
April 21st, 2008 at 12:47 pm
Thanks for posting this. It may be very useful. But there's one thing I don't like - just cloning the h2 elements into the page contents
div.I think it might be much better to use ordered list, and put each link to
lielement, to keep it more semantic. I modified the code and posted it on my blog, with a description (and a link back to your article, of course. don't bother about the language, it's Polish ;)) and prepared a working example.btw. Great blog, I've learned quite much here, thanks!
April 24th, 2008 at 3:55 pm
Excellent post, muchas gracias y saludos desde Chile!!