Revised: Table of Contents for SharePoint Wiki Pages

It’s more then two years ago when I first wrote a table of contents script to enhance wiki pages in SharePoint. Time to release a new version and this time it’s a jQuery plugin. This new version will work potentially work with all versions of SharePoint and Office 365. I just have tested it in SharePoint 2013 and Office 365.
Now I also included support to for all levels of headlines (<h1> – <h6>).
This is possible because a found a good old part in the jQuery Documentation

Table Of Contents - Live in Action

Table Of Contents – Live in Action

:header selector

This selector is not supported by css but jQuery adds support of this pseudo selector. It is a really handy extension because you can select all headlines on a page or just the headlines in parts of your page, for example an article. The support of this pseudo selector was added in jQuery 1.2 almost seven years ago.

The first code snippet selects all headlines on a page. The second only for example in an article.

Table of Content – jQuery plugin

To make all the code reusable for SharePoint I decided to write a jQuery plugin calls “sp.tableOfContents.js”. This plugin has several option that helps you to configure the table of contents easily.

The options

orderedList: Defines if the output should contain <ul> (false) or <ol> (true)
customStyle: adds a custom style sheet class to the table of contents menu
attachTo: defines the element where your table of content should be added to use ID or class selector
prepend: defines if you like the table of content to be prepended (true) or appended (false) to the previous defined element
headlineText: gives your table of contents nice looking label

The usage

To use this script make sure that jQuery and my table of content jQuery plugin is registered in your SharePoint Pages, on an article page or page layout.
The code to make it work looks like this:

I haven’t added all the options in the sample script above but feel free to play around with it. Additional improvements to this scripts are also welcome.

Download or Link

If you like to make it look like on the screenshot you can use the following css file:

Additional Readings


  1. Hi – Great article! Could you please help a n00b and explain in more detail how to use this … Thanks!

  2. Hi Stefan,
    thank you for creating this table of Content. I’m looking for a while to find a solution like this for the wiki sites, but I don’t get it. 🙁
    I’m not an developer and try to add your code, but nothing happened. I tried to add the code with the Skript Editor Webpart and I tried the procedure from the links that you posted to the other comment, but nothing happend. Can you please explain it step by step, how I have to handle your code, js and css files?
    Thank you very much!

    • Hi Mario, I will add detailed configuration instructions next week so that you and Kevin can use it without any problem or additional configuration effort.

  3. Detailed instructions would be much appreciated indeed! I was tasked with creating a knowledge base for my team in Sharepoint online and I’m looking at various ways that this could be accomplished. Wiki definitely seems to be the way to go, but I can’t figure out the navigation / landing page (basically we always need to have a navigation visible on the left of all wiki pages). This script seems to be what we would need, I’m looking forward to the more detailed instructions and testing it out.

    Thank you for your efforts!

  4. I tried this with your minimized JS script – nothing happened. I switched to the non-minimized script and everything works well!
    I’ve used the code references in the master page code.
    A big hand of thank you for you.

    • Oh and this doesn’t seem to apply for pages created under Wiki Page Library. Probably due to the missing RHS space reserved for Categories of the Enterprise wiki page.

      • Yeah you are right this is because of the right hand space in the enterprise wiki. I basically built it that you are able to attach it anywhere but not explicitly tested. In a couple of days I will provide a how-to and will also take look into the problem you told me with the minified version.

        Thank you very much for your feedback so far.

  5. Dear Stefan!
    Just want to say – thanks a lot for your work and sharing this script, it works good, you’ve really saved me!
    Best regards,

  6. Hi! First let me say thanks! After adjusting the attachTo variable (I’m working with a team-site wiki on O365), this works like a charm!

    I did notice however, that another “chapter” child element is generated on each header, every time the page is viewed. The functionality remains the same, and only one list item is created in the TOC, but the source HTML looks nuts after a few page views. Adding an “if not exists” to your For loop will solve this. Here’s how I adjusted it:
    for(var i = 0; i < headers.length; i++){
    if($(headers[i]).children('a[name ^= "chapter"]').length == 0){
    var nameLink = "“;
    var tmpHeader = $(headers[i]).prepend(nameLink);

  7. Stefan, have you tried the ToC with following or similar order of leveling: h1, h2, h2, h3, h3, h2, h3, h3? In my case the second “h2, h3, h3” sort of breaks on ToC as it gets intended to the very left (the level h1 titles should be).

    • Actually I think I fixed that by changing the sp.tableOfContents-1.0.js followingly: Changed (“”).repeat(lvlCounter) to “” (around row 87). The script used to repeat (or ) tags too many times closing the list. Hopefully this doesn’t break anything now!

      • .. HTML was formatted out, so: changed the bracketed +listTag+ .repeat(lvlCounter) to bare +listTag+, as the closing ol or ul tags repeated unnecessarily.

  8. Stefan,
    Great little tool. It got me thinking though, how hard would it be to make some code leveraging the same concept that dynamically appended the TOC for multiple links listed on a wiki page (or manually defined links). A bit like creating an index for a set of wiki pages by just supplying the link to the page.
    Happy to consider remuneration to achieve such a capability.

    • In general you shouldn’t jump more than one level on an HTML Web Site. This is the reason why I haven’t tested for. In think in this code you will probably face an issue if you jump from H1 directly into an H4. Jumps from H4 to H1 shouldn’t be a problem.


  9. I’v modified TOC to set ID’s to header elements instead of adding elements, which is obselete in HTML5 (

    However this depends on to generate slugs.
    Also introduced code that makes it possible to use TOC links on another pages: if hashtag is present, we scrollIntoView when id’s are generated. But it won’t work for headers with identical names as those will append some random id. If someone wants to improve, share the code afterwards 🙂


    • Thank you for your update to my code.


  10. I faced the same problems with jumping (for example) from H4 to H1 as Christopher did.
    Jumps in this “direction” are pretty normal in documents. The other way around not so much, i would say.
    Unfortunately this solution did not work for me. I have no idea why.

    So here is my (not pretty, but working) fix:
    // Loop through headline
    for(var i = 0; i < headers.length; i++){

    var nameLink = "“;

    var tmpHeader = $(headers[i]).prepend(nameLink);

    if(prevTagName !== null && tmpHeader.prop(‘tagName’) < prevTagName){

    // BM – Jump back more than one level
    if(tmpHeader.prop('tagName')=="H1" && prevTagName=="H3") {tocElements += ("”);}
    if(tmpHeader.prop(‘tagName’)==”H1″ && prevTagName==”H4″) {tocElements += (“”);}

    if(tmpHeader.prop(‘tagName’)==”H1″ && prevTagName==”H5″) {tocElements += (“”);}
    if(tmpHeader.prop(‘tagName’)==”H1″ && prevTagName==”H6″) {tocElements += (“”);}
    if(tmpHeader.prop(‘tagName’)==”H2″ && prevTagName==”H4″) {tocElements += (“”);}

    if(tmpHeader.prop(‘tagName’)==”H2″ && prevTagName==”H5″) {tocElements += (“”);}

    if(tmpHeader.prop(‘tagName’)==”H2″ && prevTagName==”H6″) {tocElements += (“”);}
    if(tmpHeader.prop(‘tagName’)==”H3″ && prevTagName==”H5″) {tocElements += (“”);}

    if(tmpHeader.prop(‘tagName’)==”H3″ && prevTagName==”H6″) {tocElements += (“”);}
    if(tmpHeader.prop(‘tagName’)==”H4″ && prevTagName==”H6″) {tocElements += (“”);}

    tocElements += (“”);
    lvlCounter -= 1;

    } else if(prevTagName !== null && tmpHeader.prop(‘tagName’) > prevTagName){
    tocElements += “”;
    lvlCounter += 1;

    tocElements += ““+tmpHeader.text()+”\n”;
    prevTagName = tmpHeader.prop(‘tagName’);

    Thanks to Stefan for providing this lifesaver of a script.
    Greetings from Vienna.

  11. This TOC is great. Have used it much.
    Now I noticed a minor issue; if there are two content editors on the page there will be two TOCs one below another. This is happening because when there are two content editors there are two class=”ms-rtestate-field” tags on the page.
    I think it shouldn’t be working like this. Any ideas if there would be an easy fix to this? Tried to figure out something but no success so far

    • Thank you for pointing out. You basically need to find the that the headline is inside a web part editor and ignore it.
      It’s a really interesting bug you spoted. Might come up with a fix this week. Source will be available on github

Comments are closed.