Feature request: table of contents

Hi all, I’d like to request that for Ghost-powered themes and Ghost Pro users, that we are provided access to a table of contents solution in our blogs and newsletters. This is not uncommon in the content world and shows professionalism to readers as well as allows them an easy way to navigate through long-form content.

Evidence that this is required is from Ghost themselves: they also use this solution for themselves as seen in all of their own tutorials, here is one example Open a Ghost theme in a code editor

I understand that there is a tutorial here. The problem is for non-coders and nontechies who are purposely using Ghost Pro and Ghost-powered themes so that they don’t have to do this kind of work is that its not an option How to add a table of contents to your Ghost site

If you’d like to see Ghost add a table of content, please vote above. Thank you.

I agree it’d be nice if this were built-in.

Having said that, this is doable with just code injection, if editing .hbs files isn’t possible or desirable. Which theme are you working in? Is this something you need? I have a perpetual case of “oh, that’d be an interesting challenge”, so if you pointed me at the theme (and at your actual site might be a good idea, too), I’d probably make you a “paste this one thing here here for instant TOC” thing just for the fun of it.

2 Likes

Thanks Cathy, my issue with code injections and the reason I don’t like WordPress is its a bolt-on solution. So every time there is an update to the theme, all my bolted on bits and pieces disappear and I need to re-add them. I use Ghost pro and the Casper theme.

I’ve switched themes and reinstalled themes and never had my code injection or any other site settings (except custom ones) disappear. I’m self-hosted, so maybe it’s different. If you change the .hbs, then yes, you have to be careful to not lose your changes, but I’d expect a code injection solution to survive an upgrade (probably without needing any changes).

2 Likes

ah, you know better than me I guess.
I still think it needs a Ghost solution for dumbasses like me.

To reiterate @Cathy_Sarisky’s point: Code Injection does persist between theme changes and upgrades, so you never need to worry about those modifications being lost.

1 Like

Really? That’s great if it does.
Still, I think the point stands, for non-techies, a table of contents is a great idea. Ghost themselves use it on their own blog.

1 Like

Yes, this would be really useful for longer posts and could be automated based on the distribution of H1 and H2 headers through the piece. I am non-techie so having this as a new content block would be nice, rather than trying to figure out/commission code injections.

Biron Themes built this into their theme and you can choose to show or not show ToC’s on a post by post basis as there are multiple post templates to choose from. See the Nikko theme for example.

1 Like

I would like to see this as a default feature and optional setting in post/page creation

2 Likes

Automatic table of content generation would be an awesome feature for more lengthy, complex posts.

2 Likes

Yes. I like this feature. It can also be done using anchor links, but would be much easier to achieve the desired result faster if this feature was built-in.

This would be super useful if it were natively built in for all Ghost Official themes. Praying for this and native SEO :pray:t2:

Hi all, not sure where is the best place to implement TOC on the backend or fontend. Current Ghost developers better know it. But since I cannot do it on the backend (for a many reasons) I’ve spent some time to implement super simple script which builds TOC for any pages on the frontend and also it supports multi-levels.

I’m raising this topic because Ghost dev team could use either fully my script publicly available here or could use it as an ispiration for something similar and better integrated to the Ghost.

The only thing, that I think they will need to handle is support for different themes.

By adding script to the header of the blog you only need to inject this html in the needed place of your article: <toc title="Table of Contents"></toc>

IDK if it’s a good idea to leave links to my personal blog, but here are the two my articles related to this topic and where I describe how to use my script:

Will be glad to answer any questions.

P.S. It’s not about “hey, see my blog and use my script”. I’m sharing my experience and hope that dev team will build a native solution. I also want it to be available for my articles as a toggle or something more user friendly than html code as it’s today

I Created a Code That Will Do All of This for You

I will beak it down for the non-coders in the forum and those with experience for ease of editing and implementing it into your website.

Note: To those of you who are experts, please remember I am far from it. So I may use incorrect terminology in this post, and there may have been a way better way to set this up. But I spent an entire day working this code out, and it’s tried and true. Can’t say if there is a better way to have it work, but for what I needed, it works great.


Functionality Overview:
The code I’ve created is designed to automatically generate a Table of Contents (ToC) for your posts. It scans through your content, finds all the headers (like section titles), and then builds the ToC for you.

Additionally, I’ve included another piece of code that you can use to add a Frequently Asked Questions (FAQ) section to your posts. This FAQ code is structured using something called “Microdata” and “Schema markup,” which helps search engines like Google understand and display your content better.

Here’s How It Works:

  • Automatic Table of Contents: The code will look through your post and automatically find all the section titles (headers) h1, h2, and h3 title tags, and then it will then create a Table of Contents at the top of your post, and link to that section of the page that take readers directly to those sections.

  • Post Only: The code is made to function only on post and not pages, by checking to make sure the template is a post and not a page. This was done so you can choose in inject the table of contents code into the entire site, instead of post by post. However, you can do it on a post by post basis by saving the codes as HTML snippets (more on that in a bit)

  • FAQ Integration (If You Want It): If you want to include an FAQ section, you can use the FAQ code I’ve provided. The script that builds the Table of Contents will recognize this FAQ section, add a link to it at the bottom of the Table of Contents, but it won’t include every single question from the FAQ in the ToC—just the title of the FAQ section itself.

For Non-Coders:

  • If You Want to Use Both: You can use both the Table of Contents code and the FAQ code together. They work well with each other, and the Table of Contents will automatically include a link to the FAQ section without listing all the questions individually.

  • If You Only Want One: If you only want a Table of Contents and not an FAQ, you can ignore the FAQ part of the code completely. It won’t affect how the Table of Contents works. Similarly, if you only want an FAQ and not a Table of Contents, you can skip the ToC code. Both codes work independently, so using one won’t mess up the other. I will tell you how to implement them at the very bottom.

Detailed Overview:

For those of you with coding experience, I’ve provided a detailed explanation of how the codes work together. For everyone else, below that, I’ll walk you through step-by-step instructions on how to set it up on your website. There are two options depending on what you need: you can use just one of the codes or both together.


This explanation should help non-coders understand the functionality and flexibility of the code without needing any technical background.
Certainly!


For Those With Coding Knowledge

A detailed explanation of the codes

1. FAQ Code Explanation

The FAQ code is designed to display a list of frequently asked questions in a structured and SEO-friendly format. It uses schema markup to improve visibility in search engine results and allows users to interact with the content by clicking on questions to reveal the answers.

Key Features of the FAQ Code:

  • Schema Markup (Microdata): The code uses the FAQPage schema from Schema.org to indicate that the content contains a list of questions and answers. This helps search engines recognize the content as a FAQ, potentially displaying it prominently in search results.
  • Collapsible Questions: Each FAQ question (<h3>) can be clicked to toggle the visibility of its corresponding answer. This is handled by a JavaScript function (toggleFAQ(id)), which changes the display of the answer between hidden and visible.
  • HTML Structure: The FAQ is wrapped in a div with structured data attributes (itemscope, itemtype), ensuring that search engines can easily parse the questions and answers.

2. Table of Contents (ToC) Script Explanation

The ToC script dynamically generates a Table of Contents based on the headers (<h2>, <h3>, and <h4>) found within an article. It can also automatically include a nested FAQ section if FAQs are present in the article. However, the script is designed to work perfectly fine even if no FAQ section is present.

How the ToC Script Works:

  • Initialization:

    • The script starts by creating strings toc and faqContent to hold the HTML for the Table of Contents and FAQ section, respectively.
    • It initializes counters (itemCount and faqCount) to track how many headers and FAQs are processed.
  • Header Detection and Processing:

    • The script scans all <h2>, <h3>, and <h4> elements within the article.
    • Each header is assigned a unique ID (if it doesn’t already have one), which is then used to create anchor links for navigation within the document.
    • The script checks if a header has the faq class:
      • If it does, it categorizes that header as part of the FAQ section and appends it to the faqContent string.
      • If it does not, the header is treated as a regular section and added to the ToC.
  • HTML Insertion:

    • After processing all headers, the script inserts the generated ToC into the document, just before the first <h2> header, or at the beginning of the article if no <h2> is found.
    • If no FAQ headers are detected, the faqContent string remains empty, and the FAQ dropdown section is not included in the final HTML.

Interaction Between the FAQ Code and ToC Script:

  • Conditional Inclusion of FAQ: The ToC script is built to check for FAQ headers automatically. If it finds headers marked as FAQs, it will include them in a nested dropdown within the ToC. If no FAQ headers are detected, the script simply ignores this part, and the ToC functions normally without any FAQ section.

  • Versatility: Because the ToC script is not dependent on the presence of an FAQ, you can use the same script on pages with or without FAQs. The ToC will always be generated based on the headers present in the article. If there’s no FAQ section, the ToC will list only the regular sections of the article, ensuring the page remains functional and user-friendly.

  • SEO and Usability: Even without an FAQ, the ToC enhances user navigation and improves SEO by providing structured data that search engines can use to better index the page. The FAQ code, when present, further enhances this by creating rich snippets that might appear prominently in search results.

Summary:

  • Independent Functionality: The ToC script works independently of the FAQ section. If no FAQs are found, the script simply generates a standard Table of Contents without including a FAQ dropdown, ensuring the script is versatile and can be used across various pages, whether they contain FAQs or not.

  • Automatic Integration: When FAQs are present, they are seamlessly integrated into the ToC as a nested dropdown. If not, the script ignores the FAQ portion, focusing solely on the headers present, maintaining a clean and functional ToC.

  • Enhanced User Experience and SEO: Regardless of whether the FAQ is included, the ToC provides clear navigation, helping users easily find content, while the FAQ (when present) offers additional value both to users and in terms of SEO.

This design allows you to use the same ToC script across different pages, with or without FAQs, providing flexibility in content management and consistent user experience across your site.


Table of Contents Code

<script>
document.addEventListener("DOMContentLoaded", function() {
    // Check if the body has the class 'post-template'
    if (document.body.classList.contains('post-template')) {
        var toc = `<details><summary>Table of Contents</summary><nav id='toc' role="navigation" aria-label="Table of contents" itemscope itemtype="http://schema.org/ItemList"><ul>`;
        var faqContent = `<details><summary>Frequently Asked Questions</summary><ul>`; // Start of FAQ nested dropdown
        var itemCount = 0;
        var faqCount = 0;

        document.querySelectorAll("article h2, article h3, article h4").forEach(function(header, index) {
            var id = header.id || header.textContent.trim().toLowerCase().replace(/\s+/g, '-').replace(/[^\w\-]/g, '');
            if (!header.id) header.id = id;  // Assign an ID if the header doesn't have one
            
            if (header.classList.contains("faq")) { // Check if the header is part of FAQs
                faqCount++;
                faqContent += `<li class='faq-${header.tagName.toLowerCase()}' itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">` +
                              `<meta itemprop="position" content="${faqCount}" />` +
                              `<a href='#${id}' itemprop="item"><span itemprop="name">${header.textContent}</span></a></li>`;
            } else {
                itemCount++;
                toc += `<li class='${header.tagName.toLowerCase()}' itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">` +
                       `<meta itemprop="position" content="${itemCount}" />` +
                       `<a href='#${id}' itemprop="item"><span itemprop="name">${header.textContent}</span></a></li>`;
            }
        });

        faqContent += `</ul></details>`; // End of FAQ nested dropdown
        toc += `</ul></nav></details><meta itemprop="numberOfItems" content="${itemCount}" hidden>`;

        // Find the first <p> tag in the article that doesn't have the 'gh-article-excerpt' class
        var firstParagraph = document.querySelector("article p:not(.gh-article-excerpt)");
        if (firstParagraph) {
            firstParagraph.insertAdjacentHTML("afterend", toc);
        }
    }
});
</script>
<!-- FAQ HTML Code With Schema Markup -->
<div itemscope itemtype="http://schema.org/FAQPage" style="margin-bottom: 15px;">
  <h2 itemprop="name" class="faq-title" style="margin-bottom: 15px;">Frequently Asked Questions</h2>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq1')" style="cursor: pointer;">What holidays in Albania cause hotel prices to increase?</h3>
    <div id="faq1" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        During religious holidays like **Easter** and **Christmas**, many Albanians living abroad return home, leading to increased domestic travel. This influx can cause hotel prices, especially in major cities like Tirana, Shkoder, and Berat, to rise significantly.
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq2')" style="cursor: pointer; margin-top: 15px;">Which festivals in Albania can cause a spike in accommodation costs?</h3>
    <div id="faq2" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        The **Tirana International Film Festival** in September is one of the key events that can lead to a surge in hotel prices. Additionally, smaller regional festivals that attract significant crowds can also drive up costs in their respective areas.
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq3')" style="cursor: pointer; margin-top: 15px;">What is the cheapest month to visit Albania?</h3>
    <div id="faq3" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        **November** is often the cheapest month to visit Albania. The summer crowds have gone, and the winter chill hasn't fully set in, making it a low-demand period for travel.
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq4')" style="cursor: pointer; margin-top: 15px;">How can I save money on accommodations in Albania?</h3>
    <div id="faq4" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        You can save money on accommodations in Albania by:
        <ul>
          <li>Traveling during the off-peak seasons (late autumn, winter, early spring).</li>
          <li>Booking hotels well in advance, especially in popular destinations like Saranda or Tirana.</li>
          <li>Considering guesthouses or Airbnb options, particularly in less touristy areas.</li>
          <li>Staying in smaller towns or villages, which generally have lower accommodation costs.</li>
        </ul>
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq5')" style="cursor: pointer; margin-top: 15px;">Is it expensive to eat out in Albania?</h3>
    <div id="faq5" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        Eating out in Albania is generally affordable compared to other European countries. Prices can vary depending on the location:
        <ul>
          <li>In Tirana or tourist hotspots, a meal at a mid-range restaurant might cost between **€10-€15** per person.</li>
          <li>In smaller towns or rural areas, you can enjoy a meal for as little as **€5-€8** per person.</li>
        </ul>
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq6')" style="cursor: pointer; margin-top: 15px;">When is the best time to visit Albania for a budget traveler?</h3>
    <div id="faq6" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        The best time for budget travelers to visit Albania is during the **late autumn (October to November)** or **early spring (March to May)**. These periods offer lower accommodation and flight prices, fewer tourists, and moderate weather ideal for exploring.
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq7')" style="cursor: pointer; margin-top: 15px;">Are flights to Albania cheaper during specific months?</h3>
    <div id="faq7" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        Yes, flights to Albania tend to be cheaper during the off-peak travel months of **November** and **February**. Booking flights during these months, especially in advance, can result in significant savings.
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq8')" style="cursor: pointer; margin-top: 15px;">What are some tips for budget-friendly travel in Albania?</h3>
    <div id="faq8" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        To travel in Albania on a budget, consider the following tips:
        <ul>
          <li>Use public transportation like buses and furgons (shared minibuses) instead of taxis.</li>
          <li>Visit free attractions like national parks, historic sites, and beaches.</li>
          <li>Shop at local markets for fresh produce and souvenirs.</li>
          <li>Stay in budget accommodations like hostels, guesthouses, or Airbnb.</li>
        </ul>
      </div>
    </div>
  </div>

  <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Question">
    <h3 itemprop="name" class="faq" onclick="toggleFAQ('faq9')" style="cursor: pointer; margin-top: 15px;">How does the cost of transportation in Albania compare to other countries?</h3>
    <div id="faq9" itemprop="acceptedAnswer" itemscope itemtype="http://schema.org/Answer" style="display: none; margin-top: 15px;">
      <div itemprop="text">
        The cost of transportation in Albania is generally lower compared to many Western European countries. Local buses and shared minibuses (furgons) are inexpensive, with fares typically ranging from **€1-€5** for intercity travel. Taxis are also affordable but more expensive than public transport.
      </div>
    </div>
  </div>

</div>

<script>
function toggleFAQ(id) {
  var element = document.getElementById(id);
  if (element.style.display === 'none') {
    element.style.display = 'block';
  } else {
    element.style.display = 'none';
  }
}
</script>
<style>
    /* Styles for TOC and FAQ */
    details > summary {
        font-size: 24px;
        color: #333;
        cursor: pointer;
        text-align: left;
        font-weight: bold;
        padding: 5px;
        margin-top: 10px;
    }
    #toc, .faq {
        width: 100%;
        max-width: 960px;
        margin: auto;
        padding: 15px;
        background: #f9f9f9;
        border: 1px solid #ccc;
        box-sizing: border-box;
    }
    #toc ul, .faq ul {
        list-style-type: none; /* No bullet points for clearer hierarchy */
        padding: 0;
        margin-top: 5px;
        margin-left: 0;
    }
    #toc li, .faq li {
        margin-bottom: 5px;
        padding-left: 10px;
    }
    #toc li.h2, .faq li.faq-h2 {
        font-size: 18px;
        font-weight: bold;
    }
    #toc li.h3, .faq li.faq-h3 {
        font-size: 16px;
        padding-left: 20px;
    }
    #toc li.h4, .faq li.faq-h4 {
        font-size: 14px;
        padding-left: 40px;
    }
    #toc a, .faq a {
        color: #003399;
        text-decoration: none;
    }
    #toc a:hover, .faq a:hover {
        text-decoration: underline;
    }
    details[open] #toc, details[open] .faq {
        display: block;
    }
    @media (max-width: 768px) {
        #toc, .faq {
            padding: 10px;
        }
        #toc li.h3, #toc li.h4, .faq li.faq-h3, .faq li.faq-h4 {
            margin-left: 10px;
        }
    }
</style>

How Put On Website For Non-Coders

Step 1: Go to Settings
Step 2: Code Injection Setting
Step 3: Footer
Step 4: Place Style Code In Footer

Then for step 5 you have options for the table of contents. You can simply inject it in the footer where you did the style code if you want it on all post pages. If you do not, read on.

You are going to take the Table of Contents code and do the same if you want it to automatically add a table of contents to every post on your page. However, if there will be post where you do not want a table of contents. Create an HTML snippet in a post and save it. You then simply add that snippet to the post when you want a TOC. It only takes a second add it, and this will prevent a TOC from being placed on every single post.

However, you can still put it on the entire site and control which post it’s shown on with some modification of the code, to where it’s only shown on post with certain tags by instructing the code to only place the table of contents on post that contain a certain tag, by targeting the meta data that looks like this in your source code

<meta property="article:tag" content="YOUR_TAG_NAME">

I like to work as little as possible so my code is to work on all post unless it contains a specific set of tags. But a person could create a tag called TOC, and then tell the code to work on all post that contain that tag, and then add that tag to the post you want the TOC to show up on, leaving you in full control of where it goes.

For the FAQ code, you must create an HTML Snippet and save the code.
You will then place the snippet on all post at the bottom or wherever you want the FAQ to show up. Click on the snippet to either manually edit the questions and answers before posting.
Or copy the code and paste it into a free chat GPT account, and tell chat GPT to analyze the FAQ code and stand by. Then copy your blog post into chat gpt, and tell it to change the FAQ for questions and answers that pertain to your post and SEND IT! It will give you back the code in about 10 seconds with all new questions and answers, ready for you to post on your blog and publish.

This is the best way I have found to fully or nearly automate great functionality into a managed Ghost pro website, and add some valuable SEO Schema and user experience to the website.

I hope you all enjoy! If this should be placed in a different location admin, feel free to move it where it can help the most people if you think it’s of value to the community.

If you want to see how it all looks on a post before doing all of this, here is a recent blog post I did about places to see in Maui, other than Lahaina, that shows the TOC, FAQ and their styling.

2 Likes