Are you happy with your logging solution? Would you help us out by taking a 30-second survey? Click here

contents

Table of contents generator.

Subscribe to updates I use contents


Statistics on contents

Number of watchers on Github 392
Number of open issues 14
Average time to close an issue 3 days
Main language JavaScript
Average time to merge a PR about 7 hours
Open pull requests 3+
Closed pull requests 2+
Last commit about 4 years ago
Repo Created about 5 years ago
Repo Last Updated over 1 year ago
Size 1.27 MB
Organization / Authorgajus
Contributors3
Page Updated
Do you use contents? Leave a review!
View open issues (14)
View contents activity
View on github
Fresh, new opensource launches 🚀🚀🚀
Trendy new open source projects in your inbox! View examples

Subscribe to our mailing list

Evaluating contents for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

Table of Contents (TOC) Generator

Travis build status NPM version Bower version

Table of contents generator.

Usage

Quick Start

var Contents,
    contents,
    newHeading;

Contents = require('contents');

// If you are using ./dist/ version, then Contents is available under "gajus" global property, i.e.
// Contents = gajus.Contents;

// This example generates a table of contents for all of the headings in the document.
// Table of contents is an ordered list element.
contents = Contents();

// Append the generated list element (table of contents) to the container.
document.querySelector('#your-table-of-contents-container').appendChild(contents.list());

// Attach event listeners:
contents.eventEmitter().on('change', function () {
    console.log('User has navigated to a new section of the page.');
});

// The rest of the code illustrates firing "resize" event after you have
// added new content after generating the table of contents.
newHeading = document.createElement('h2');
hewHeading.innerHTML = 'Dynamically generated title';

document.body.appendChild(newHeading);

// Firing the "resize" event will regenerate the table of contents.
contents.eventEmitter().trigger('resize');

Examples

The code for all of the examples is in the examples folder.

Raise an issue if you are missing an example.

Introduction of ES6 in 4.0.0

Similar Libraries stats have been generated in 22-Nov-14 08:44:41 UTC. Since then Contents has evolved a lot. The source code is written in ES6 and depends on babel-core to run. In projects that already depend on Babel and use webpack to build packages, this is not going to be a problem. Other projects need to consider the relatively heavy weight of the generated package.

Similar Libraries

Feature contents toc jquery.tocify.js
Markup using nested <ol> - -
Smooth scrolling -
Forward and back button support -
Events - -
Efficient scroll event -
Reflect window resize -
Extract table of contents as an array - -
Overwrite markup and navigation - -
Can have multiple on a page
Required 3rd party libraries - jQuery jQuery, jQueryUI
Size < 6.000 kb 2.581 kb 7.246 kb
GitHub Stars 192 307 435

Last updated: Saturday, 22-Nov-14 08:44:41 UTC.

Required 3rd Party Libraries

There are no 3rd party dependencies. jQuery selectors are used in the examples to make it simple for the reader.

Smooth Scrolling

You can implement smooth scrolling using either of the existing libraries. See Integration Examples.

Window Resize and `scroll` Event Handling

The library will index offsetTop of all articles. This index is used to reflect the change event. The index is built upon loading the page, and in response to window.onresize and ready events.

Reading offsetTop causes a reflow. Therefore, this should not be done while scrolling.

Table of Contents Array

You can extract the table of contents as a collection of nested objects representing the table of contents.

/**
 * @return {array} Array representation of the table of contents.
 */
contents.tree();

Tree is a collection of nodes:

[
    // Node
    {
        // Hierarchy level (e.g. h1 = 1)
        level: 1,
        // Id derived using articleId() function.
        id: '',
        // Name derived using articleName() function.
        name: '',
        // The article element.
        element: null,
        // Collection of the descendant nodes.
        descendants: [ /* node */ ]
    }
]

Download

Using NPM:

npm install contents

Using Bower:

bower install contents

Configuration

Name Type Description
articles NodeList, jQuery (optional) The default behavior is to index all headings (H1-H6) in the document. See Content Indexing.
link function (optional) Used to represent article in the table of contents and to setup navigation. See Linking.

Content Indexing

The default behavior is to index all headings (H1-H6) in the document.

Use articles setting to index content using your own selector:

gajus
    .contents({
        articles: document.querySelectorAll('main h2, main h2')
        // If you are using jQuery
        // articles: $('main').find('h2, h3').get()
    });

Hierarchy

articles will be used to make the table of contents. articles have level of importance. The level of importance determines list nesting (see Markup). For HTML headings, the level of importance is derived from the tag name (<h[1-6]>). To set your own level of importance, use Contents.level dataset property or jQuery data property with the same name, e.g.

$('main').find('.summary').data('gajus.contents.level', 4);

gajus
    .contents({
        articles: $('main').find('h1, h2, h3, .summary').get()
    });

When level of importance cannot be determined, it defaults to 1.

Linking

link method is used to represent article in the table of contents and to setup navigation. This method is called once for each article after the list of the table of contents is generated.

The default implementation:

  1. Derives ID from the article
  2. Generates a hyperlink using article ID as the anchor
  3. Appends the URL to the table of contents
  4. Wraps the article node in a self-referencing hyperlink.
/**
 * This function is called after the table of contents is generated.
 * It is called for each article in the index.
 * Used to represent article in the table of contents and to setup navigation.
 * 
 * @param {HTMLElement} guide An element in the table of contents representing an article.
 * @param {HTMLElement} article The represented content element.
 */
Contents.link = function (guide, article) {
    var guideLink = document.createElement('a'),
        articleLink = document.createElement('a'),
        articleName = article.innerText,
        articleId = article.id || Contents.id(articleName);

    article.id = articleId;

    articleLink.href = '#' + articleId;

    while (article.childNodes.length) {
        articleLink.appendChild(article.childNodes[0], articleLink);
    }

    article.appendChild(articleLink);

    guideLink.appendChild(document.createTextNode(articleName));
    guideLink.href = '#' + articleId;
    guide.insertBefore(guideLink, guide.firstChild);
};

To overwrite the default behavior, you can provide your own link function as part of the configuration:

Contents({
    // Example of implementation that does not wrap
    // article node in a hyperlink.
    link: function (guide, article) {
        var guideLink,
            articleName,
            articleId;

        guide = $(guide);
        article = $(article);

        guideLink = $('<a>');
        articleName = article.text();
        articleId = article.attr('id') || Contents.id(articleName);

        guideLink
            .text(articleName)
            .attr('href', '#' + articleId)
            .prependTo(guide);

        article
            .attr('id', articleId);
    }
});

Article ID

The default implementation relies on each article having an id attribute to enable anchor navigation.

If you are overwriting the default link implementation, you can take advantage of the Contents.id function.

Contents.id is responsible for deriving a unique ID from the text of the article, e.g.

<h2>Allow me to reiterate</h2>
<h2>Allow me to reiterate</h2>
<h2>Allow me to reiterate</h2>

The default link implementation will use Contents.id to give each article a unique ID:

<h2 id="allow-me-to-reiterate">Allow me to reiterate</h2>
<h2 id="allow-me-to-reiterate-1">Allow me to reiterate</h2>
<h2 id="allow-me-to-reiterate-2">Allow me to reiterate</h2>

Markup

Table of contents is an ordered list element. List nesting reflects the heading hierarchy. The default behavior is to represent each heading using a hyperlink (See Linking), e.g.

<h1>JavaScript</h1>
<h2>History</h2>
<h2>Trademark</h2>
<h2>Features</h2>
<h3>Imperative and structured</h3>
<h3>Dynamic</h3>
<h3>Functional</h3>
<h2>Syntax</h2>

Contents will generate the following markup for the above content:

<ol>
    <li>
        <a href="#javascript">JavaScript</a>

        <ol>
            <li>
                <a href="#history">History</a>
            </li>
            <li>
                <a href="#trademark">Trademark</a>
            </li>
            <li>
                <a href="#features">Features</a>

                <ol>
                    <li>
                        <a href="#imperative-and-structured">Imperative and structured</a>
                    </li>
                    <li>
                        <a href="#dynamic">Dynamic</a>
                    </li>
                    <li>
                        <a href="#functional">Functional</a>
                    </li>
                </ol>
            </li>
            <li>
                <a href="#syntax">Syntax</a>
            </li>
        </ol>
    </li>
</ol>

Events

Event Description
ready Fired once after the table of contents has been generated.
resize Fired when the page is loaded and in response to resize and orientationchange window events.
change Fired when the page is loaded and when user navigates to a new section of the page.

Attach event listeners using the eventEmitter.on of the resulting Contents object:

var contents = Contents();

contents.eventEmitter.on('ready', function () {});
contents.eventEmitter.on('resize', function () {});

The change event listener is passed extra parameters: .current.article, .current.guide, and when available, .previous.article, .previous.guide:

contents.eventEmitter.on('change', function (data) {
    if (data.previous) {
        $(data.previous.article).removeClass('active-article');
        $(data.previous.guide).removeClass('active-guide');
    }

    $(data.current.article).addClass('active-article');
    $(data.current.guide).addClass('active-guide');
});

You must trigger resize event after programmatically changing the content or the presentation of the content.:

contents.eventEmitter.trigger('resize');

This is required to recalculate the position of the content.

contents open issues Ask a question     (View All Issues)
  • almost 3 years Improve package size
  • about 4 years Provide ability to specify parent container
  • over 4 years Error when article name is a UTF-8 string or contains some UTF-8 characters
  • almost 5 years Support IE8?
  • almost 5 years Option to have separator
  • almost 5 years Provide a node script to generate TOC for markdown files
  • almost 5 years Provide a directive for AngularJS
  • about 5 years Create an example of collapsible table of contents
contents open pull requests (View All Pulls)
  • Remove babel runtime dependency
  • Fix examples
  • Create README.md in Examples DIR
contents questions on Stackoverflow (View All Questions)
  • Disabling Table of Contents links in Qualtrics
  • How to convert a byte array with hexadecimal contents to string with decimal in c#
  • How to replace contents of column in one file with that of another file based on key variable?
  • How do I create a tar.gz file with the contents of the webpack output directory?
  • Using Delphi7 TClientDataSet: is it possible to have it save it's XML contents in an indented format
  • How to copy google drive folder and its contents to list of folder ID's
  • Reading contents of a file, storing it in a variable, encrypting it and storing it in a new file
  • Why can't bash assign the contents of this file to a variable?
  • Git - Ignore directories based on their contents
  • ERROR: Cell Contents indices must be greater than 0
  • Update database with contents of JTable when a check box is clicked
  • iOS dropbox sdk / core api - listing public folder contents without authorization
  • Set CALayer as SCNMaterial's diffuse contents
  • stuck on not being able to view contents of an array list
  • DevExpress Report - How to have a band expand to fit contents?
  • Revise bookmarklet to use the same copy from clipboard to process the clipboard contents and fill form
  • How to delete XML element with specific contents using PHP regexp?
  • Delete Folder Contents in Python
  • mPDF cuts the contents of the column
  • Unable to copy file contents using centos Script with cronjob on CentOS
  • jQuery Validate - Validate contents of string
  • Logging the contents to a Unix file by log4net
  • Chrome extension to modify the contents of a page
  • Expand a div that has a preview of its contents using Jquery
  • Where does Odoo save the contents of My Dashboard
  • Python web scraping using BeautifulSoup, how come my output file print out the contents three times with different format?
  • When I try to display contents on Applications folder in Terminal, I got "Chrome Apps.localized"
  • Sharepoint: How do I filter a document library view to show the contents of a subfolder?
  • Hadoop can list s3 contents but spark-shell throws ClassNotFoundException
  • Add contents of geoJson file to vector layer after download
contents list of languages used
Other projects in JavaScript