
Product
Socket Brings Supply Chain Security to skills.sh
Socket is now scanning AI agent skills across multiple languages and ecosystems, detecting malicious behavior before developers install, starting with skills.sh's 60,000+ skills.
Table of contents generator.
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');
console.log.The code for all of the examples is in the examples folder.
Raise an issue if you are missing an example.
| 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.
There are no 3rd party dependencies. jQuery selectors are used in the examples to make it simple for the reader.
You can implement smooth scrolling using either of the existing libraries. See Integration Examples.
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.
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 */ ]
}
]
Using NPM:
npm install contents
Using Bower:
bower install contents
| 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. |
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()
});
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.
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:
/**
* 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);
}
});
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>
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>
| 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.
FAQs
Table of contents generator.
The npm package contents receives a total of 4,349 weekly downloads. As such, contents popularity was classified as popular.
We found that contents demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket is now scanning AI agent skills across multiple languages and ecosystems, detecting malicious behavior before developers install, starting with skills.sh's 60,000+ skills.

Product
Socket now supports PHP with full Composer and Packagist integration, enabling developers to search packages, generate SBOMs, and protect their PHP dependencies from supply chain threats.

Security News
An AI agent is merging PRs into major OSS projects and cold-emailing maintainers to drum up more work.