contents

by gajus

gajus / contents

Table of contents generator.

406 Stars 41 Forks Last release: Not found Other 157 Commits 14 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

Table of Contents (TOC) Generator

Travis build status NPM version

Table of contents generator.

Usage

Quick Start

import Contents from 'contents';

// This example generates a table of contents for all of the headings in the document. // Table of contents is an ordered list element. const 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. const newHeading = document.createElement('h2');

newHeading.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

    | ✓ | - | - | | Smooth scrolling | - | ✓ | ✓ | | Forward and back button support | ✓ | - | ✓ | | Events | ✓ | - | - | | Efficient
    scroll
    event
    | ✓ | ✓ | heading- | | 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
    
    

    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:
    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 (
    ). 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);
    
    

    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 = (guide, article) => {
      const guideLink = document.createElement('a'),
      const articleLink = document.createElement('a'),
      const articleName = article.innerText,
      const 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: (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.

    Allow me to reiterate

    Allow me to reiterate

    Allow me to reiterate

    The default

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

    Allow me to reiterate

    Allow me to reiterate

    Allow me to reiterate

    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.

    JavaScript

    History

    Trademark

    Features

    Imperative and structured

    Dynamic

    Functional

    Syntax

    Contents will generate the following markup for the above content:

    1. JavaScript
      <ol>
        <li>
          <a href="https://github.com/gajus/contents/blob/master/#history">History</a>
        </li>
        <li>
          <a href="https://github.com/gajus/contents/blob/master/#trademark">Trademark</a>
        </li>
        <li>
          <a href="https://github.com/gajus/contents/blob/master/#features">Features</a>
      
          <ol>
            <li>
              <a href="https://github.com/gajus/contents/blob/master/#imperative-and-structured">Imperative and structured</a>
            </li>
            <li>
              <a href="https://github.com/gajus/contents/blob/master/#dynamic">Dynamic</a>
            </li>
            <li>
              <a href="https://github.com/gajus/contents/blob/master/#functional">Functional</a>
            </li>
          </ol>
        </li>
        <li>
          <a href="https://github.com/gajus/contents/blob/master/#syntax">Syntax</a>
        </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:
    const contents = Contents();
    
    

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

    The

    change
    event listener is passed extra parameters:
    .current.article
    ,
    .current.guide
    , and when available,
    .previous.article
    ,
    .previous.guide
    :
    contents.eventEmitter.on('change', (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.

    We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.