Quantcast
Channel: Grant Winney
Viewing all articles
Browse latest Browse all 348

Creating a Table of Contents for your blog

$
0
0
Creating a Table of Contents for your blog

I wrote about 5 quick hacks for your Ghost theme last year, after switching to Ghost as a blogging platform. The last hack I mentioned was generating a "table of contents" using a handlebars script I'd found. Ghost was still considered beta at the time, and when 1.0 was released, the script stopped working correctly. I never bothered going back to figure out why.

But a table of contents is nice to have, and convenient for your visitors, so I wrote a new script that should work for any html page (with minor adjustments). You can get it from GitHub too.

/**
 * For displaying a table of contents - pass the entire document (DOM) to writeTocToDocument
 */

function getHeaderLevel(header) {
    return Number(header.nodeName.slice(-1));
}

function createTocMarkup(headers) {
    var prevLevel = 1;
    var output = "";

    headers.forEach(function(h) {
        var currLevel = getHeaderLevel(h);
        if (currLevel > prevLevel) {
            var ranOnce = false;
            while (currLevel > prevLevel) {
                if (ranOnce) {
                    output += " ";
                }
                output += "<ol style=\"margin-bottom:0px\"><li>";
                prevLevel += 1;
                ranOnce = true;
            }
        } else if (currLevel == prevLevel) {
            output += "</li><li>";
        } else if (currLevel < prevLevel) {
            while (currLevel < prevLevel) {
                output += "</li></ol>";
                prevLevel -= 1;
            }
            output += "<li>";
        }

        output += `<a href="#${h.id}">${h.innerText}</a>`;
    });

    if (output != "") {
        // Change 2 to the max header level you want in the TOC; in my case, H2
        while (prevLevel >= 2) {
            output += "</li></ol>";
            prevLevel -= 1;
        }
        output = `<h2 class="widget-title">Table of Contents</h2><div style="margin-left:-10px">${output}</div>`;
    }

    return output;
}

function getTocMarkup(document) {
    // I was only interested in the headers within the element that had the .post-content class,
    // which is specific to the Ghost blog. If you're using this elsewhere, or are interested in
    // the entire document, delete this line and use document.querySelectorAll(...) on the next line.
    var body = document.getElementsByClassName('post-content')[0];
    
    // Add or remove header tags you do (or don't) want to include in the TOC
    var headers = body.querySelectorAll('h2, h3, h4, h5, h6');

    // Change the number to 1 if you want headers no matter what.
    // Or if you want at least 3 headers before generating a TOC, change it to 3.
    if (headers.length >= 2) {
        return createTocMarkup(headers);
    } else {
        return "";
    }
}

General Usage

Just call the function and write the return value out to the page.

<script type="text/javascript">
    document.write(getTocMarkup(document));
</script>

Usage in Ghost

Here's how I've got it displayed in the side bar in Ghost.

  1. Copy the above script into a file named toc.js, and drop it in the assets/js directory.

  2. Reference the file from default.hbs, somewhere between the <head></head> tags so it's available as the page loads.

    <script type="text/javascript" src="{{asset "js/toc.js"}}"></script>
    
  3. Call it from wherever you want to display it.

Those steps are generic to Ghost. Here's how I got it to work with the Wildbird theme.

  1. Modify post.hbs, near the bottom where it inserts the sidebar, so that it also passes a reference to the current page into the sidebar.

    {{!-- The tag below includes the theme sidebar - partials/sidebar.hbs --}}
    {{> sidebar this}}
    
  2. Modify the sidebar.hbs file so that, if the current page is a "post", it'll insert a new section containing your table of contents.

    {{!-- Table of Contents --}}
    {{#is "post"}}
    <section class="widget widget-text">
        {{#post}}
        <script type="text/javascript">
            document.write(getTocMarkup(document));
        </script>
        {{/post}}
    </section><!-- .widget -->
    {{/is}}
    

Snapshots

Here's how it looks when rendered.

A single-layer of headers:

Creating a Table of Contents for your blog

Two layers of headers:

Creating a Table of Contents for your blog

Multiple layers of headers:

It somewhat handles omitted headers, like going right from H2 to H5, but not really nicely.

Creating a Table of Contents for your blog

Some styling applied to remove the numbers and indent on linewrap

Creating a Table of Contents for your blog

Viewing all articles
Browse latest Browse all 348

Trending Articles