Creating a Table of Contents in Google Docs Using Google Apps Script

Google Apps Script is a powerful tool that allows you to automate tasks and extend the functionality of Google Apps. In this blog post, we will explore how to create a Table of Contents (TOC) in a Google Docs document using Google Apps Script. This is particularly useful if you have a lengthy document with multiple sections and headers, and you want to automatically generate a TOC that links to each section.

Step-by-Step Guide

1. Setting Up the Project

First, open a new Google Docs document or an existing one where you want to add the TOC. Then, open the Script Editor by navigating to Extensions > Apps Script. This will open a new tab where you can write your script.

2. Writing the Script

We will write a script that goes through the document, finds all the headers, and creates a TOC at the beginning of the document.

Here’s the complete script:

function createTableOfContents() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();

// Clear any existing TOC
clearExistingTOC(body);

// Create a paragraph for the TOC title
var tocTitle = body.insertParagraph(0, 'Table of Contents');
tocTitle.setHeading(DocumentApp.ParagraphHeading.HEADING1);

// Create a list for the TOC entries
var tocList = body.insertListItem(1, '');

// Get all the paragraphs in the document
var paragraphs = body.getParagraphs();

// Array to hold the TOC entries in order
var tocEntries = [];

// Iterate through the paragraphs and collect TOC entries for headers
for (var i = 0; i < paragraphs.length; i++) {
var paragraph = paragraphs[i];
var heading = paragraph.getHeading();

if (heading !== DocumentApp.ParagraphHeading.NORMAL) {
var text = paragraph.getText();
var position = doc.newPosition(paragraph.getChild(0), 0);
var bookmark = doc.addBookmark(position);
var link = '#' + bookmark.getId();

// Collect TOC entry
tocEntries.push({ text: text, link: link, heading: heading });
}
}

// Insert the TOC entries in order
for (var i = 0; i < tocEntries.length; i++) {
var entry = tocEntries[i];
var tocEntry = body.insertListItem(body.getChildIndex(tocList) + 1 + i, entry.text).setLinkUrl(entry.link);

// Indent TOC entry based on heading level
switch (entry.heading) {
case DocumentApp.ParagraphHeading.HEADING1:
tocEntry.setNestingLevel(0);
break;
case DocumentApp.ParagraphHeading.HEADING2:
tocEntry.setNestingLevel(1);
break;
case DocumentApp.ParagraphHeading.HEADING3:
tocEntry.setNestingLevel(2);
break;
// Add more cases if you have more heading levels
default:
tocEntry.setNestingLevel(3);
}
}
}

function clearExistingTOC(body) {
var paragraphs = body.getParagraphs();
for (var i = 0; i < paragraphs.length; i++) {
var paragraph = paragraphs[i];
if (paragraph.getHeading() === DocumentApp.ParagraphHeading.HEADING1 && paragraph.getText() === 'Table of Contents') {
body.removeChild(paragraph);
// Remove the following paragraphs until the next non-heading paragraph
while (i + 1 < paragraphs.length && paragraphs[i + 1].getHeading() !== DocumentApp.ParagraphHeading.NORMAL) {
body.removeChild(paragraphs[++i]);
}
}
}
}

3. Explanation of the Script

  • Getting the Active Document: var doc = DocumentApp.getActiveDocument(); retrieves the current Google Docs document.
  • Clearing Existing TOC: The clearExistingTOC function removes any previous TOC to avoid duplicates.
  • Inserting TOC Title: var tocTitle = body.insertParagraph(0, 'Table of Contents'); inserts a title for the TOC at the beginning of the document and sets its heading style.
  • Finding Headers: The script iterates through all the paragraphs in the document. For each paragraph that is a heading (not a normal paragraph), it gets the text and creates a bookmark.
  • Creating TOC Entries: For each heading, it adds an entry to the TOC list with a link to the corresponding section in the document. The nesting level of the entry is determined by the heading level.

4. Running the Script

After writing the script, save your project with a relevant name. Then, you can run the createTableOfContents function by clicking the play button in the toolbar. The script will prompt you to authorize access to your Google Docs if this is your first time running it.

5. Result

The script will automatically generate a TOC at the beginning of your document, with links to each section based on the headers. This TOC will make it easier to navigate through your document, especially if it is lengthy and contains multiple sections.

Conclusion

Using Google Apps Script to automate the creation of a Table of Contents in Google Docs is a powerful way to enhance document navigation and organization. With just a few lines of code, you can streamline your workflow and improve the readability of your documents. Try customizing the script further to fit your specific needs, such as adding more heading levels or adjusting the TOC formatting.

function createTableOfContents() {
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();

  // Clear any existing TOC
  clearExistingTOC(body);

  // Create a paragraph for the TOC title
  var tocTitle = body.insertParagraph(0, 'Table of Contents');
  tocTitle.setHeading(DocumentApp.ParagraphHeading.HEADING1);

  // Create a list for the TOC entries
  var tocList = body.insertListItem(1, '');

  // Get all the paragraphs in the document
  var paragraphs = body.getParagraphs();

  // Array to hold the TOC entries in order
  var tocEntries = [];

  // Iterate through the paragraphs and collect TOC entries for headers
  for (var i = 0; i < paragraphs.length; i++) {
    var paragraph = paragraphs[i];
    var heading = paragraph.getHeading();

    if (heading !== DocumentApp.ParagraphHeading.NORMAL) {
      var text = paragraph.getText();
      var position = doc.newPosition(paragraph.getChild(0), 0);
      var bookmark = doc.addBookmark(position);
      var link = '#' + bookmark.getId();

      // Collect TOC entry
      tocEntries.push({ text: text, link: link, heading: heading });
    }
  }

  // Insert the TOC entries in order
  for (var i = 0; i < tocEntries.length; i++) {
    var entry = tocEntries[i];
    var tocEntry = body.insertListItem(body.getChildIndex(tocList) + 1 + i, entry.text).setLinkUrl(entry.link);

    // Indent TOC entry based on heading level
    switch (entry.heading) {
      case DocumentApp.ParagraphHeading.HEADING1:
        tocEntry.setNestingLevel(0);
        break;
      case DocumentApp.ParagraphHeading.HEADING2:
        tocEntry.setNestingLevel(1);
        break;
      case DocumentApp.ParagraphHeading.HEADING3:
        tocEntry.setNestingLevel(2);
        break;
      // Add more cases if you have more heading levels
      default:
        tocEntry.setNestingLevel(3);
    }
  }
}

function clearExistingTOC(body) {
  var paragraphs = body.getParagraphs();
  for (var i = 0; i < paragraphs.length; i++) {
    var paragraph = paragraphs[i];
    if (paragraph.getHeading() === DocumentApp.ParagraphHeading.HEADING1 && paragraph.getText() === 'Table of Contents') {
      body.removeChild(paragraph);
      // Remove the following paragraphs until the next non-heading paragraph
      while (i + 1 < paragraphs.length && paragraphs[i + 1].getHeading() !== DocumentApp.ParagraphHeading.NORMAL) {
        body.removeChild(paragraphs[++i]);
      }
    }
  }
}